dir_picker

Platform License: MIT

A Flutter plugin for picking a directory across all platforms using native system dialogs. Returns a SelectedLocation? — null if the user cancelled.

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 WebSelectedLocation wrapping a FileSystemDirectoryHandle. Use .handle to access directory contents via the File System Access API. SelectedLocation.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 SelectedLocation? location = await DirPicker.pick();

if (location == null) {
  print('Cancelled');
} else if (location is WebSelectedLocation) {
  // 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}');
}

Platform Options

Each platform exposes its own options class for customizing the dialog. Pass them to DirPicker.pick():

final location = await DirPicker.pick(
  androidOptions: const AndroidOptions(shouldPersist: true),
  macosOptions: const MacosOptions(acceptLabel: 'Choose', message: 'Select a project folder'),
  linuxOptions: const LinuxOptions(title: 'Select Folder', acceptLabel: 'Choose'),
  windowsOptions: const WindowsOptions(title: 'Select Folder', acceptLabel: 'Choose'),
);

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
NativeLocation Native platforms — use .uri to get the selected directory URI.
WebSelectedLocation 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 WebSelectedLocation) {
  final web.FileSystemDirectoryHandle handle = location.handle;
  // list files, read contents, etc.
}

Custom dialog labels

final location = await DirPicker.pick(
  macosOptions: const MacosOptions(
    acceptLabel: 'Use This Folder',
    message: 'Select the folder to import from',
  ),
  linuxOptions: const LinuxOptions(
    title: 'Import Folder',
    acceptLabel: 'Use This Folder',
  ),
  windowsOptions: const WindowsOptions(
    title: 'Import Folder',
    acceptLabel: 'Use This Folder',
  ),
);

Android — enable persistent permission

final location = await DirPicker.pick(
  androidOptions: const AndroidOptions(shouldPersist: true),
);

API Reference

DirPicker.pick

static Future<SelectedLocation?> pick({
  AndroidOptions? androidOptions,
  LinuxOptions? linuxOptions,
  MacosOptions? macosOptions,
  WindowsOptions? windowsOptions,
})

Returns a SelectedLocation (either NativeLocation or WebSelectedLocation), or null if the user cancelled.

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.

Libraries

dir_picker