unified_file_picker logo

unified_file_picker

pub package License: MIT

unified_file_picker — cross-platform explorer-style file and folder picker for Flutter

Cross-platform file and folder picker for Flutter with a unified, fully customizable explorer-style UI.
Published by quds.cc

Instead of relying on each platform's native dialog, unified_file_picker renders one consistent Flutter interface on Android, iOS, Windows, Linux, and macOS — with list/details/tiles/icons views, recursive search, keyboard shortcuts, thumbnails, and RTL support.


Table of contents


Features

Core capabilities

  • Open files - single or multiple selection with preset or custom type filters
  • Pick folders - navigate into subfolders on desktop; confirm current folder or selected folders
  • Save files - choose destination folder, edit file name, optional overwrite confirmation
  • Write bytes - persist data to a path returned by the save dialog

Explorer UI

  • Breadcrumb path bar - click segments to navigate; click the current segment or background to type/paste a full path (Enter navigates, Escape cancels)
  • Navigation history - back, forward, and go-up controls
  • Quick-access sidebar - Home, Desktop, Documents, Downloads, Pictures, Videos, Music, and drives/volumes (layout adapts to available width)
  • Four view modes -list, details (sortable table), tiles, and icons with zoom controls
  • Recursive search - search within the current folder tree with progress and cancel (desktop)
  • Thumbnails - image and video previews where supported
  • Context menu - sort by name/modified/type/size; switch view mode (desktop right-click)
  • Marquee selection - drag to select multiple items (desktop, multi-select modes)
  • Animated folder transitions - smooth content changes when navigating

Customization

  • Full theming - colors, typography, modal dimensions, sidebar width, per-kind icon overrides
  • Presentation - modal dialog or fullscreen route with configurable enter/exit animations
  • Localization - English and Arabic built-in; override any string per session
  • RTL - layout direction support via textDirection or app locale
  • Adaptive chrome - Material on most platforms; Cupertino navigation on iOS/macOS

Platform support

Platform Status Notes
Windows Full dart:io backend; This PC root; drive list with volume labels and free/total space
Linux Full XDG user directories; root and mounted volumes
macOS Full Home shortcuts; ~/Movies for Videos
iOS Full Photos library via native plugin; phasset:// URIs; permission handling
Android Partial Native plugin is a stub — gallery/filesystem integration is not yet implemented

Desktop platforms use a pure Dart dart:io backend and do not require native filesystem code. Mobile platforms use a platform channel (unified_file_picker) for gallery access and permissions.


Installation

Add to your pubspec.yaml:

dependencies:
  unified_file_picker: ^0.1.3

Then run:

flutter pub get

Requirements: Dart SDK ^3.12.2, Flutter >=3.3.0.


Setup

Register the localization delegate in your app (required for built-in UI strings):

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:unified_file_picker/unified_file_picker.dart';

MaterialApp(
  localizationsDelegates: const [
    UnifiedFilePickerLocalizations.delegate,
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
  ],
  supportedLocales: const [
    Locale('en'),
    Locale('ar'),
  ],
  // ...
);

Quick start

Pick files

final files = await UnifiedFilePicker.pickFiles(
  context,
  type: FilePickerType.images,
  selection: SelectionMode.multiple,
);

if (files != null) {
  for (final file in files) {
    print('${file.name} → ${file.path}');
  }
}

Pick a folder

final folders = await UnifiedFilePicker.pickFolder(context);

if (folders != null && folders.isNotEmpty) {
  print('Selected: ${folders.first.path}');
}

Save a file

final location = await UnifiedFilePicker.saveFile(
  context,
  suggestedFileName: 'report.pdf',
  type: FilePickerType.documents,
);

if (location != null) {
  final success = await UnifiedFilePicker.writeBytes(location, pdfBytes);
}

Full control with options

final result = await UnifiedFilePicker.pick(
  context,
  options: FilePickerOptions(
    mode: FilePickerMode.open,
    type: FilePickerType.images,
    selection: SelectionMode.multiple,
    initialViewMode: FilePickerViewMode.tiles,
    showThumbnails: true,
    presentation: FilePickerPresentation.modal,
    theme: const FilePickerTheme(
      title: 'Choose photos',
      primaryColor: Color(0xFF5B4FCF),
      borderRadius: 16,
    ),
  ),
);

API overview

UnifiedFilePicker

Method Returns Description
pick(context, options:) List<PickedFile>? Opens the picker with full FilePickerOptions configuration
pickFiles(context, …) List<PickedFile>? Convenience wrapper for open-file mode
pickFolder(context, …) List<PickedFile>? Convenience wrapper for folder mode
saveFile(context, …) PickedFile? Save dialog; returns a single destination
writeBytes(location, bytes) bool Writes bytes to a path from saveFile

Presentation helpers

Function Description
showUnifiedFilePicker(context, options:) Dispatches modal or fullscreen based on options.presentation
showUnifiedFilePickerModal(context, options:) Shows a centered dialog with theme constraints
showUnifiedFilePickerRoute(context, options:) Pushes a fullscreen PageRoute

Options factory

Use FilePickerOptionsFactory for opinionated defaults, then copyWith for overrides:

final options = FilePickerOptionsFactory.openFiles(
  type: FilePickerType.documents,
  selection: SelectionMode.single,
).copyWith(
  initialDirectory: '/home/user/Documents',
  barrierDismissible: false,
);

Picker modes

FilePickerMode.open

Pick one or more files from the filesystem or mobile gallery.

  • Filter by FilePickerType preset or custom FileTypeFilter list
  • Optional thumbnails for images and videos
  • Single-click to select, double-click to open on desktop

FilePickerMode.folder

Pick one or more folders.

  • On desktop: single-click selects, double-click enters a folder
  • Use this folder action confirms the current directory without selecting a child
  • Folder filter dropdown is hidden

FilePickerMode.save

Choose a destination folder and file name.

  • Pre-fills suggestedFileName
  • File-type filter can append the correct extension
  • Optional overwrite confirmation when confirmExistingFile is true (default)

Selection

enum SelectionMode {
  single,   // one item at confirm
  multiple, // Ctrl/Cmd+click, Shift+range, marquee, Select All
}

File type filters

Preset types (FilePickerType)

Type Description
any All files
documents PDF, Office, text, markdown, JSON, XML, HTML, CSV, …
images JPG, PNG, GIF, WebP, HEIC, SVG, TIFF, …
videos MP4, MOV, AVI, MKV, WebM, …

Custom filters

await UnifiedFilePicker.pickFiles(
  context,
  customFilters: const [
    FileTypeFilter(
      label: 'Markdown',
      extensions: ['.md', '.markdown'],
      mimeTypes: ['text/markdown'],
    ),
    FileTypeFilter(
      label: 'Archives',
      extensions: ['.zip', '.tar', '.gz'],
    ),
  ],
  initialFilter: /* optional FileTypeFilter */,
);

When customFilters is non-empty, it replaces the default filter derived from FilePickerType.


View modes

Configure via initialViewMode and allowedViewModes:

Mode Description
list Compact rows with icon and name
details Sortable table — name, modified date, type, size
tiles Medium square tiles in a zoomable grid
icons Large square icons in a zoomable grid

Tiles and icons modes include a zoom slider (0.7×–1.8×). Restrict available modes:

allowedViewModes: [
  FilePickerViewMode.list,
  FilePickerViewMode.details,
],

Explorer UI

  • Shows the current path as clickable segments
  • Click any segment to jump to that folder
  • Click the last segment or empty area to enter edit mode — type or paste a full path
  • Enter navigates immediately; Escape cancels
  • On Windows, paths display with backslashes (e.g. C:\Users\…)

Quick-access sidebar

When showShortcutsSidebar is true (default):

  • Quick access — OS-specific shortcuts (Home, Desktop, Documents, Downloads, Pictures, Videos, Music)
  • This PC — drives and mounted volumes
  • Wide layouts show a vertical sidebar; compact layouts use a horizontal chip bar

On Windows, the empty path shows This PC with drive tiles including volume label, capacity bar, and free/total space.

Recursive folder search is available on desktop when browsing a real directory (not at the This PC root):

  • Type in the search bar to scan subfolders
  • Progress indicator and stop button during search
  • Skips hidden and build directories (.git, node_modules, build, …)

Sorting

Sort folder contents by name, modified, type, or size (ascending/descending):

  • Click column headers in details view
  • Use the right-click context menu on desktop

Directories always sort before files.

Selection behavior

Platform Behavior
Desktop Single-click selects; double-click opens folder or confirms file
Mobile Single tap activates
Multi-select Ctrl/Cmd+click toggles; Shift+click range-selects; marquee drag on desktop

The confirm button shows the selection count in multi-select mode.


Keyboard shortcuts

Shortcut Action
Escape Dismiss the picker
Escape (path edit) Cancel path editing
Enter (path edit) Navigate to typed path
Ctrl/Cmd + A Select all (multi-select, non-save modes)
Ctrl/Cmd + Click Toggle item in multi-select
Shift + Click Range select (desktop)

Theming

Pass a FilePickerTheme to any pick method or embed it in FilePickerOptions:

const theme = FilePickerTheme(
  primaryColor: Color(0xFF5B4FCF),
  backgroundColor: Color(0xFF1E1E2E),
  textColor: Colors.white,
  borderRadius: 16,
  modalMaxWidth: 1024,
  modalMaxHeight: 720,
  sidebarWidth: 240,
  title: 'Choose files',
  confirmLabel: 'Open',
  cancelLabel: 'Cancel',
  iconOverrides: {
    FileEntryKind.folder: Icons.folder_special_rounded,
    FileEntryKind.drive: Icons.storage_rounded,
  },
);

Customizable properties

Category Properties
Colors primaryColor, backgroundColor, surfaceColor, textColor, subtitleColor, selectedColor, sidebar colors, per-kind icon colors, dividerColor, barrierColor
Modal modalMaxWidth/Height, modalMinWidth/Height, modalElevation, modalInsetPadding, modalBorderSide, modalShadowColor
Layout borderRadius, sidebarWidth, useGridWhenThumbnails
Typography titleTextStyle, bodyTextStyle, subtitleTextStyle, sectionTitleStyle
Labels title, confirmLabel, cancelLabel
Icons iconOverrides — map of FileEntryKindIconData

Unset values inherit from the host app's ThemeData via mergeWithTheme. The picker also builds a harmonized ThemeData for internal Material widgets.

Presentation and transitions

FilePickerOptions(
  presentation: FilePickerPresentation.modal, // or .fullscreen
  transition: FilePickerTransition(
    kind: FilePickerTransitionKind.slideUp, // fade, slideFromStart, scale
    duration: Duration(milliseconds: 280),
    curve: Curves.easeOutCubic,
  ),
  barrierDismissible: true,
)

Localization

Built-in locales:

Locale Language
en English
ar Arabic (RTL)

Override strings per session

Extend UnifiedFilePickerStrings (or EnglishFilePickerStrings) and pass via FilePickerOptions.strings:

class BrandStrings extends EnglishFilePickerStrings {
  const BrandStrings();

  @override
  String get selectFiles => 'Choose attachments';

  @override
  String get thisPc => 'My Computer';
}

await UnifiedFilePicker.pick(
  context,
  options: FilePickerOptions(
    strings: const BrandStrings(),
    textDirection: TextDirection.rtl, // optional explicit direction
  ),
);

The strings interface covers titles, action buttons, sidebar sections, sorting/view labels, search messages, path bar hints, save/overwrite dialogs, empty states, and permission prompts.


Embedding the picker

Use FilePickerScreen to embed the explorer inside your own navigation instead of a modal:

FilePickerScreen(
  options: myOptions,
  // Optional: inject a custom FileSystemService for testing
)

Or call the lower-level helpers directly:

await showUnifiedFilePickerModal(context, options: myOptions);
await showUnifiedFilePickerRoute(context, options: myOptions);

Saving files

Typical workflow

// 1. Show save dialog
final location = await UnifiedFilePicker.saveFile(
  context,
  suggestedFileName: 'export.csv',
  type: FilePickerType.documents,
  confirmExistingFile: true,
);

if (location == null) return; // user cancelled

// 2. Write data
final ok = await UnifiedFilePicker.writeBytes(location, csvBytes);

Additional helpers

// Copy an existing file to the save location
await SaveFileHelper.copyFrom(location, '/path/to/source.pdf');

// Check whether the target already exists
final exists = await SaveFileHelper.exists(location);

On save, the file-type filter can automatically append the correct extension via ensureExtension.


Return values

Method Cancelled Success
pick / pickFiles / pickFolder null Non-empty List<PickedFile>
saveFile null Single PickedFile

PickedFile fields

Field Description
path Absolute filesystem path or platform URI (content://, phasset://)
name Display file or folder name
size Size in bytes when known
mimeType MIME type hint
isDirectory true for folder selections

Example app

The example/ app is a live customization demo. Run it from the plugin root:

cd example
flutter pub get
flutter run

Or use the example launch config in .vscode/launch.json.

The demo includes:

  • Live embedded preview of FilePickerScreen
  • Theme controls — accent color, dark mode, corner radius, modal size, sidebar width
  • Presentation and transition pickers
  • View mode and thumbnail toggles
  • Custom strings and icon overrides
  • English ↔ Arabic locale toggle with RTL
  • Pick files, pick folder, and save file actions with result display

See example/README.md for additional run options and troubleshooting.


License

MIT — see LICENSE.


Publishing to pub.dev

This package is ready for publication. Brand assets live in doc/ (logo.png, poster.png).

pub.dev images: README images must use absolute https:// URLs (e.g. raw.githubusercontent.com). Relative paths like doc/logo.png work on GitHub but not on pub.dev.

Before running flutter pub publish:

  1. Log indart pub login (use the Google account that will own the package).
  2. Verify the package — from the plugin root:
 flutter pub get
 flutter analyze
 flutter test
 flutter pub publish --dry-run
  1. Publish — when the dry run passes:
    flutter pub publish
    
  2. Tag the release on GitHub (required for pub.dev repository verification):
    git tag v0.1.3
    git push origin main --tags
    
    pub.dev checks that pubspec.yaml on GitHub matches the published package and that the repository URL is correct.
  3. Optional verified publisher — to display quds.cc as the verified publisher on pub.dev, complete domain verification and add publisher: quds.cc back to pubspec.yaml.

Note: The example app uses publish_to: 'none' and is bundled for demonstration only — it is not published separately.

Libraries

unified_file_picker
Cross-platform file and folder picker with a unified, customizable Flutter UI.