dir_picker 0.4.1 copy "dir_picker: ^0.4.1" to clipboard
dir_picker: ^0.4.1 copied to clipboard

A Flutter plugin for picking directories and listing file system entries, supporting Android, iOS, macOS, Windows, Linux, and Web via FFI and JNI.

dir_picker #

Platform License: MIT

A Flutter plugin for picking a directory across all platforms using native system dialogs. Returns a PickedLocation? — null if the user cancelled, and can list entries from a previously picked directory with DirPicker.listEntries().

Features #

  • 📁 All Platforms – Android, iOS, macOS, Windows, Linux, and Web
  • Native Performance – Powered by FFI (iOS/macOS/Windows) and JNI (Android) for near-zero overhead
  • 🎨 Customizable Dialogs – Platform-specific options for title, button labels, and more
  • 🔗 Persistent Permissions – Android SAF persistent URI access across reboots
  • 🐧 Linux Portals – XDG Desktop Portal with zenity/kdialog fallback

If you want to say thank you, star us on GitHub or like us on pub.dev.

Installation #

First, follow the package installation instructions and add dir_picker to your app.

Quick Start #

Platform Setup #

Android Configuration

Supported: API 21+ (Android 5.0+)

No configuration needed. The plugin uses the Storage Access Framework (Intent.ACTION_OPEN_DOCUMENT_TREE), which grants URI access through the system picker UI — no manifest permissions required.

iOS Configuration

Supported: iOS 13.0+

No configuration needed. The plugin uses UIDocumentPickerViewController, which is available without extra entitlements.

macOS Configuration

Supported: macOS 10.15+

Add the following entitlement to macos/Runner/Release.entitlements and macos/Runner/DebugProfile.entitlements:

<key>com.apple.security.files.user-selected.read-write</key>
<true/>
Windows Configuration

Supported: Windows 10+

No configuration needed. The plugin uses the native IFileOpenDialog COM API.

Linux Configuration

The plugin tries the following dialog backends in order:

  1. XDG Desktop Portal – Works on all modern desktop environments via D-Bus (org.freedesktop.portal.FileChooser)
  2. zenity – GNOME fallback
  3. kdialog – KDE fallback

If none are available, the pick call throws. To install a fallback manually:

# GNOME
sudo apt install zenity

# KDE
sudo apt install kdialog
Web Configuration

No configuration needed. The plugin uses the File System Access API.

Browser support: Chrome 86+ and Edge 86+. Not supported in Firefox or Safari.

Note: On web, pick() returns a WebPickedLocation wrapping a FileSystemDirectoryHandle. Use .handle to access directory contents via the File System Access API. PickedLocation.uri is always null on web — browsers do not expose full filesystem paths. Requires package:web in your app's dependencies to work with the handle directly.

Basic Usage #

import 'package:dir_picker/dir_picker.dart';

final PickedLocation? location = await DirPicker.pick();

if (location == null) {
  print('Cancelled');
} else if (location is WebPickedLocation) {
  // Web: use handle to access directory contents via File System Access API
  // Requires package:web in your app's dependencies
  final handle = location.handle; // FileSystemDirectoryHandle
  print('Selected directory: ${location.name}');
} else {
  // Native (Android, iOS, macOS, Windows, Linux)
  print('Selected: ${location.uri}');
}

List directory entries #

final location = await DirPicker.pick();
if (location == null) return;

final entries = await DirPicker.listEntries(location, recursive: true);
for (final entry in entries) {
  print('${entry.relativePath} (dir: ${entry.isDirectory})');
}

Platform Options #

Pass a PickOptions to DirPicker.pick() to customize the dialog for the current platform:

final location = await DirPicker.pick(
  options: PickOptions.android(shouldPersist: true),
);

final location = await DirPicker.pick(
  options: PickOptions.macos(acceptLabel: 'Choose', message: 'Select a project folder'),
);

final location = await DirPicker.pick(
  options: PickOptions.linux(title: 'Select Folder', acceptLabel: 'Choose'),
);

final location = await DirPicker.pick(
  options: PickOptions.windows(title: 'Select Folder', acceptLabel: 'Choose'),
);

Options for other platforms are silently ignored — only the one matching the current platform is applied.

AndroidOptions #

Parameter Type Default Description
shouldPersist bool true Take persistable URI permission so the app retains access across reboots (SAF takePersistableUriPermission).

MacosOptions #

Parameter Type Default Description
acceptLabel String 'Select' Label for the confirmation button (NSOpenPanel.prompt).
message String 'Choose a directory' Descriptive text shown inside the panel (NSOpenPanel.message).

LinuxOptions #

Parameter Type Default Description
title String 'Select Directory' Window title of the dialog.
acceptLabel String 'Select' Label for the confirmation button (XDG Portal and zenity only).

WindowsOptions #

Parameter Type Default Description
title String 'Select Directory' Window title of the dialog (IFileDialog::SetTitle).
acceptLabel String 'Select' Label for the confirmation button (IFileDialog::SetOkButtonLabel).

Core Concepts #

Return value #

Result Meaning
IOPickedLocation Native platforms — use .uri to get the selected directory URI.
WebPickedLocation Web — use .handle (FileSystemDirectoryHandle) to access directory contents. .uri is null.
null The user cancelled.

Native Mechanisms #

Platform Mechanism
Android SAF (Intent.ACTION_OPEN_DOCUMENT_TREE) via JNI
iOS UIDocumentPickerViewController via FFI
macOS NSOpenPanel via FFI
Windows IFileOpenDialog (COM) via pure Dart FFI
Linux XDG Desktop Portal → zenity → kdialog
Web window.showDirectoryPicker() (JS interop)

Common Use Cases #

Simple directory pick (native) #

final location = await DirPicker.pick();
if (location != null) {
  print('Selected: ${location.uri}');
}

Simple directory pick (web) #

import 'package:dir_picker/dir_picker.dart';
import 'package:web/web.dart' as web; // required to use FileSystemDirectoryHandle

final location = await DirPicker.pick();
if (location is WebPickedLocation) {
  final web.FileSystemDirectoryHandle handle = location.handle;
  // list files, read contents, etc.
}

Custom dialog labels #

final location = await DirPicker.pick(
  options: PickOptions.macos(
    acceptLabel: 'Use This Folder',
    message: 'Select the folder to import from',
  ),
);

// or on Linux/Windows:
final location = await DirPicker.pick(
  options: PickOptions.linux(
    title: 'Import Folder',
    acceptLabel: 'Use This Folder',
  ),
);

Android — enable persistent permission #

final location = await DirPicker.pick(
  options: PickOptions.android(shouldPersist: true),
);

API Reference #

DirPicker.pick #

static Future<PickedLocation?> pick({PickOptions? options})

Returns a PickedLocation (either IOPickedLocation or WebPickedLocation), or null if the user cancelled.

options is a PickOptions sealed class — pass the platform-specific subclass using its factory constructor (e.g. PickOptions.android(...), PickOptions.macos(...)). Options for other platforms are ignored.

DirPicker.listEntries #

static Future<List<FileSystemEntry>> listEntries(
  PickedLocation location, {
  bool recursive = false,
})

Lists files and directories inside a previously picked location.

Behavior:

  • Returns a flat list of descendants under the picked root.
  • Excludes the picked root itself.
  • Lists only immediate children when recursive is false.
  • Lists all nested descendants when recursive is true.
  • Does not guarantee output ordering.

PickedLocation #

Base type returned by DirPicker.pick().

Type Platform Notes
IOPickedLocation Android, iOS, macOS, Windows, Linux Provides a non-null uri.
WebPickedLocation Web Provides a FileSystemDirectoryHandle; uri is always null.

FileSystemEntry #

Model returned by DirPicker.listEntries().

Field Type Description
name String Basename of the entry.
relativePath String Path relative to the picked root, always using / separators.
isDirectory bool Whether the entry is a directory.
uri Uri? Native URI when available; always null on web.
size int? File size in bytes; null for directories or when unavailable.
lastModified DateTime? Last modified timestamp when available.

PickOptions #

Platform-specific options passed to DirPicker.pick().

Factory Platform Purpose
PickOptions.android() Android Controls whether SAF URI permission should be persisted.
PickOptions.macos() macOS Customizes the open panel accept label and message.
PickOptions.linux() Linux Customizes dialog title and accept label.
PickOptions.windows() Windows Customizes dialog title and accept label.

Platform Support #

Android iOS macOS Windows Linux Web

Minimum versions:

  • Flutter ≥ 3.3.0
  • Dart SDK ≥ 3.6.0
  • Kotlin 2.1.0
  • Swift 5.9
  • Android API 21+
  • iOS 13.0+
  • macOS 10.15+
  • Windows 10+
  • Web: Chrome/Edge 86+

Contributing #

Contributions are welcome! Please feel free to submit a Pull Request.

License #

MIT License — see LICENSE file for details.

3
likes
150
points
212
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for picking directories and listing file system entries, supporting Android, iOS, macOS, Windows, Linux, and Web via FFI and JNI.

Repository (GitHub)
View/report issues

Topics

#dir-picker #directory #picker #ffi #jni

License

MIT (license)

Dependencies

dbus, ffi, flutter, flutter_web_plugins, jni, path, web

More

Packages that depend on dir_picker

Packages that implement dir_picker