flutter_native_gallery
A native photo and video gallery picker for Flutter with album browsing, pagination, ordered multiple selection, Live Photo detection, image optimization, and media metadata extraction.
Features
- Requests native photo library permissions.
- Reads albums, photos, videos, and available native metadata.
- Presents a responsive, draggable gallery bottom sheet.
- Opens album selection in a full-height page with safe-area handling.
- Loads large galleries incrementally.
- Supports image-only, video-only, or combined media requests.
- Preserves the order of multiple selections.
- Limits selection to 30 items by default with a configurable maximum.
- Detects Live Photos on supported Apple platforms.
- Shows video duration and Live Photo indicators.
- Provides a configurable HD selection control.
- Adapts the HD control to light, dark, and custom application color schemes.
- Optimizes non-HD images with configurable quality and dimensions.
- Preserves EXIF data when supported.
- Extracts dimensions, dates, duration, MIME type, location, camera, exposure, favorite state, Live Photo state, and serializable EXIF values.
- Exposes configurable labels for localization.
- Provides both a ready-to-use picker and a lower-level gallery controller.
Platform Requirements
iOS
Photo library access requires NSPhotoLibraryUsageDescription in Info.plist:
<key>NSPhotoLibraryUsageDescription</key>
<string>Photo library access is used to select media.</string>
NSPhotoLibraryAddUsageDescription is only required when selected media can
also be written back to the library:
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Photo library access is used to save media.</string>
Android
Gallery access uses the media permissions supported by each Android version:
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
ACCESS_MEDIA_LOCATION allows coordinates to be read when they are present and
the operating system grants access.
Basic Usage
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter_native_gallery/flutter_native_gallery.dart';
Open the picker and receive the selected assets in selection order:
final List<FlutterNativeGalleryAsset>? selectedAssets =
await FlutterNativeGalleryPicker.show(
context,
config: const FlutterNativeGalleryPickerConfig(
title: 'Recents',
albumPickerTitle: 'Select an album',
cancelLabel: 'Cancel',
highQualityLabel: 'HD',
highQualityTooltip: 'High quality',
sendLabel: 'Select',
requestType: FlutterNativeGalleryRequestType.imagesAndVideos,
maxSelection: 30,
showHighQualityToggle: true,
highQualityInitiallySelected: false,
),
);
A null result means the picker was dismissed. An empty gallery, denied
permission, and limited-library access are handled by the picker UI.
Export Selected Media
Resolve an asset to a file together with its normalized metadata:
for (final FlutterNativeGalleryAsset asset
in selectedAssets ?? const <FlutterNativeGalleryAsset>[]) {
final FlutterNativeGalleryAssetExport? exported = await asset.export();
if (exported == null) continue;
final File file = exported.file;
final Map<String, dynamic> metadata = exported.toJson();
}
Videos and HD images use their original file. Non-HD images use the default 1920 x 1920 bounds at quality 82. An original file is retained when optimization would produce a larger result.
A Live Photo is a paired still image and motion resource. The single-file export
contains the still image only, so its serialized metadata reports
isLivePhoto: false and preserves the native source state as
originalIsLivePhoto: true. This prevents consumers from treating a static
JPEG or HEIF export as playable Live Photo media.
Customize non-HD image export with FlutterNativeGalleryImageExportOptions:
final FlutterNativeGalleryAssetExport? exported = await asset.export(
imageOptions: const FlutterNativeGalleryImageExportOptions(
quality: 76,
maxWidth: 1600,
maxHeight: 1600,
format: FlutterNativeGalleryImageFormat.automatic,
preserveExif: true,
useOriginalWhenOptimizedFileIsLarger: true,
),
);
The highQuality argument overrides the value selected in the picker for an
individual export:
final FlutterNativeGalleryAssetExport? original = await asset.export(
highQuality: true,
);
Asset Metadata
Read metadata without exporting or optimizing the file:
final FlutterNativeGalleryAssetMetadata metadata = await asset.metadata();
final bool hasCoordinates = metadata.hasCoordinates;
final String? device = metadata.device;
final String? format = metadata.format;
final Map<String, dynamic> json = metadata.toJson();
Metadata is best effort. Values unavailable from the operating system or source
file remain null or are omitted from toJson().
Available metadata includes:
| Category | Values |
|---|---|
| Identity | Asset id, file name, media type, and relative path. |
| Media | Width, height, aspect ratio, duration, MIME type, format, and file size. |
| Dates | Capture, creation, and modification dates. |
| Native state | Orientation, subtype, favorite, trashed, and Live Photo state. |
| Location | Latitude, longitude, and altitude when available. |
| Camera | Make, model, lens, software, color space, and exposure values. |
| EXIF | Serializable metadata entries exposed by the source file. |
Asset API
Each FlutterNativeGalleryAsset provides:
| Member | Description |
|---|---|
id |
Native asset identifier. |
title |
Native title or asset id fallback. |
type |
Image, video, audio, or other. |
width / height |
Oriented media dimensions. |
aspectRatio |
Width-to-height ratio when dimensions are valid. |
duration |
Video duration. |
createdAt / modifiedAt |
Native asset dates when available. |
mimeType |
Native MIME type when available. |
isLivePhoto |
Live Photo state on supported Apple platforms. |
highQualitySelected |
HD choice captured when the selection is returned. |
file() |
Resolves the original or an optimized image file. |
export() |
Resolves a file and its normalized metadata. |
metadata() |
Reads metadata without requiring export. |
thumbnail() |
Returns thumbnail bytes at the requested size and quality. |
Request thumbnail bytes directly:
final Uint8List? thumbnail = await asset.thumbnail(
width: 320,
height: 320,
quality: 80,
);
Picker Configuration
FlutterNativeGalleryPickerConfig controls selection, presentation, and every
visible label.
| Option | Default | Description |
|---|---|---|
controller |
null |
Optional controller used for permissions and gallery access. |
requestType |
imagesAndVideos |
Loads images, videos, or both. |
maxSelection |
30 |
Maximum ordered selection count. |
pageSize |
80 |
Number of assets requested per page. |
initialHeightFactor |
0.58 |
Initial picker height. |
minHeightFactor |
0.34 |
Minimum drag extent before dismissal. |
maxHeightFactor |
0.96 |
Maximum expanded height. |
showHighQualityToggle |
true |
Shows the HD control. |
highQualityInitiallySelected |
false |
Initial HD state. |
title |
Select attachments |
Picker title when no album name is available. |
albumPickerTitle |
Select an album |
Album selection title. |
cancelLabel |
Cancel |
Close action accessibility label. |
highQualityLabel |
HD |
Visible quality label. |
highQualityTooltip |
High quality |
Quality control accessibility label. |
sendLabel |
Send |
Selection confirmation label. |
recentsLabel |
Recents |
Recent-media fallback label. |
emptyLabel |
No media found |
Empty gallery message. |
permissionTitle |
Gallery access needed |
Permission state title. |
permissionMessage |
Allow photo library access to choose media. |
Permission state message. |
openSettingsLabel |
Open settings |
Settings action label. |
limitedAccessLabel |
Manage selected photos |
Limited-library action label. |
Controller API
FlutterNativeGalleryController exposes gallery access without the picker UI:
const FlutterNativeGalleryController controller =
FlutterNativeGalleryController();
final FlutterNativeGalleryPermissionStatus permission =
await controller.requestPermission();
if (permission.canReadGallery) {
final List<FlutterNativeGalleryAlbum> albums = await controller.albums();
}
Albums expose their native count, cover, and paginated assets. The controller also provides limited-library management, settings access, and cache clearing.
Platform Behavior
Live Photo detection uses the native asset subtype available on iOS and macOS.
Android does not expose the same subtype, so isLivePhoto remains false.
Detection describes the native library asset; it does not imply that a
single-file export contains the paired motion resource.
Limited photo access can restrict albums, files, and metadata. Coordinates and EXIF values can also be absent even when permission is granted.
License
MIT License. See LICENSE.