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
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:
- macOS —
NSOpenPanelandNSSavePanelthrough Cocoa. - Windows —
IFileOpenDialogandIFileSaveDialogthrough 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:
| Platform | API | Value |
|---|---|---|
| Linux X11 / XWayland | TaoWindow.x11PortalParent or xdgPortalParent() | x11:<lowercase-hex-xid> |
| Linux Wayland | TaoWindow.xdgPortalParent() / exportXdgForeignHandle() | wayland:<xdg_foreign token> |
| macOS | TaoWindow.nsWindowHandle | owning NSWindow* (not the Compose NSView*) |
| Windows | TaoWindow.nativeHandle | HWND |
// 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
- Automatic GraalVM metadata — how the plugin bundles reachability metadata for known libraries.
- Native spell check — another native-backed ecosystem library.
- Ecosystem overview — libraries that pair with Nucleus.