Nucleus
Ecosystem

Native file dialogs

Use FileKit for native open, save, and folder pickers, with GraalVM metadata bundled by the Nucleus plugin.

Compose Multiplatform doesn't include file dialogs, and Nucleus doesn't add its own. Use FileKit for native open, save, and folder pickers. The Nucleus Gradle plugin ships GraalVM reachability metadata for FileKit, so the pickers run under native-image without extra configuration.

Add the dependency

build.gradle.kts
dependencies {
    implementation("io.github.vinceglb:filekit-compose:<version>")
}

Show a file picker

Create a launcher with rememberFilePickerLauncher and call launch() from a click handler:

val launcher = rememberFilePickerLauncher(
    type = PickerType.File(extensions = listOf("png", "jpg")),
    title = "Pick an image",
) { file ->
    // file: PlatformFile?
}

Button(onClick = { launcher.launch() }) {
    Text("Pick image")
}

The callback receives a PlatformFile?, which is null when the user cancels. PlatformFile is FileKit's cross-platform file handle, with read, write, and metadata helpers.

How it works

FileKit calls the native dialog on each operating system:

  • macOSNSOpenPanel and NSSavePanel through Cocoa.
  • WindowsIFileOpenDialog and IFileSaveDialog through JNA COM.
  • Linux — the xdg-desktop-portal FileChooser, with GTK and zenity fallbacks.

It supports type filters with descriptions; single-file, multi-file, folder, and save modes; and a Compose API (rememberFilePickerLauncher) built around the PlatformFile handle.

Parent a dialog to the Tao window

From 2.3.1, Tao exposes the platform identities FileKit and native dialogs need so pickers attach to your window instead of floating independently:

PlatformAPIValue
Linux X11 / XWaylandTaoWindow.x11PortalParent or xdgPortalParent()x11:<lowercase-hex-xid>
Linux WaylandTaoWindow.xdgPortalParent() / exportXdgForeignHandle()wayland:<xdg_foreign token>
macOSTaoWindow.nsWindowHandleowning NSWindow* (not the Compose NSView*)
WindowsTaoWindow.nativeHandleHWND
// Prefer the backend-agnostic helper on Linux:
val parent = window.xdgPortalParent()
// X11 → XdgPortalParent.X11(xid); Wayland → XdgPortalParent.Wayland(export)
// Keep a Wayland export open until the portal dialog completes, then close it.
(parent as? AutoCloseable)?.close()

// macOS sheet parent (once FileKit accepts an NSWindow parent):
// window.nsWindowHandle?.let { FileKitDialogParent.macos(it) }

On Wayland, exportXdgForeignHandle() / XdgPortalParent.Wayland wraps an XdgForeignExport that must stay open for the lifetime of any portal dialog that borrows its handle. A raw wl_surface* or the Compose NSView* is not a valid portal / sheet parent.

Run under GraalVM native image

The Nucleus Gradle plugin bundles reachability metadata for FileKit as part of its library metadata bundle. The metadata is included only when io.github.vinceglb.filekit is on the runtime classpath, and covers:

  • macOS Foundation proxy and callback types (FoundationLibrary, ID, runnable callbacks).
  • Windows JNA COM bindings (FileDialog, FileOpenDialog, FileSaveDialog, ShellItem, Shell32, COMDLG_FILTERSPEC, PROPERTYKEY).
  • The Linux xdg-desktop-portal D-Bus proxy (FileChooserDbusInterface).

When you build with the Nucleus plugin and target native-image, the dialogs run without a manual reachability-metadata.json or a tracing-agent pass.

Notes

FileKit already wraps each native dialog and integrates with Compose state, so Nucleus depends on it instead of reimplementing the same surface.

What's next