📸 Gallery Media Picker
A powerful, high-performance Flutter media picker with an Instagram-style interface. Select images, videos, GIFs, and audio directly from the device gallery with fine-grained control over appearance, behavior, and accessibility.
Built on top of photo_manager with native-level optimizations including PMCancelToken cancellation, automatic cache management, and O(1) selection rebuilds.
💫 Support the Project
If you find this package helpful, please consider giving it a ⭐ on GitHub and liking it on pub.dev!
Your support helps improve and maintain this project! ❤️
✨ Features
- 📷 Pick single or multiple images / videos / GIFs / audio
- 🖼️ Scrollable grid with infinite loading and persistent thumbnail caching
- 🔎 Album selector (dropdown overlay style)
- 🎛️ Highly customizable UI (25+ parameters)
- ✅ Built for Android & iOS
- 🔍 Automatic GIF badge and video duration overlay
- ♿ Full accessibility support (semantic labels for screen readers)
- 🌐 i18n-ready with
GalleryMediaPickerTranslations - ⚡ O(1) selection rebuilds — selecting a photo doesn't rebuild the entire grid
- 🗑️ Native cache cleanup on dispose (
PhotoManager.clearFileCache()) - 🚫 Background task cancellation via
PMCancelTokenwhen items are deselected
🚀 Getting Started
1. Install
dependencies:
gallery_media_picker: ^0.2.0
2. Platform Requirements
| Minimum | |
|---|---|
| Flutter | >= 3.41.0 |
| Dart SDK | >= 3.11.0 |
| photo_manager | ^3.8.0 |
3. Permissions Setup
The package uses photo_manager for native media access. It is strongly recommended to use permission_handler to request permissions explicitly before displaying the picker.
Android
Add the following permissions to your AndroidManifest.xml:
<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_AUDIO"/>
On older Android versions (API < 33), you may also need:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
iOS
In your Info.plist:
<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to your photo library to select media.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Allow access to your gallery</string>
Recommended Permission Request
import 'package:permission_handler/permission_handler.dart';
Future<void> requestPermissions() async {
final status = await Permission.photos.request();
if (!status.isGranted) {
// Optionally show a dialog or redirect to app settings
}
}
⚠️ Call this function before using
GalleryMediaPicker.
4. Basic Usage
GalleryMediaPicker(
pathList: (List<PickedAssetModel> paths) {
// Handle selected media files
for (final asset in paths) {
print('Selected: ${asset.path} (${asset.type})');
}
},
appBarLeadingWidget: const Icon(Icons.close, color: Colors.white),
mediaPickerParams: MediaPickerParamsModel(
maxPickImages: 5,
singlePick: false,
mediaType: GalleryMediaType.all,
thumbnailQuality: ThumbnailQuality.high,
),
)
5. Advanced Usage (Dark Theme Example)
GalleryMediaPicker(
pathList: (List<PickedAssetModel> paths) {
context.read<MediaProvider>().setPickedFiles(paths);
},
mediaPickerParams: MediaPickerParamsModel(
appBarHeight: 50,
maxPickImages: 10,
crossAxisCount: 4,
childAspectRatio: 1,
singlePick: false,
appBarColor: Colors.transparent,
gridViewBgColor: const Color(0xFF09090B),
albumTextColor: Colors.white,
gridPadding: const EdgeInsets.all(2),
thumbnailBgColor: const Color(0xFF18181B),
thumbnailBoxFix: BoxFit.cover,
selectedAlbumIcon: Icons.check_circle_rounded,
mediaType: GalleryMediaType.image,
selectedCheckColor: Colors.white,
albumSelectIconColor: Colors.white,
selectedCheckBgColor: const Color(0xFF3B82F6),
selectedAlbumBgColor: Colors.white10,
albumDropDownBgColor: const Color(0xFF18181B),
albumSelectTextColor: Colors.white,
selectedAssetBgColor: const Color(0xFF3B82F6).withOpacity(0.2),
selectedAlbumTextColor: const Color(0xFF3B82F6),
gridViewController: _scrollController, // Use a persistent controller!
thumbnailQuality: ThumbnailQuality.high,
gridViewPhysics: const BouncingScrollPhysics(),
translations: const GalleryMediaPickerTranslations(
noMediaFound: 'No hay archivos multimedia.',
permissionDenied: 'Permisos denegados.',
),
),
)
🧩 API Reference
GalleryMediaPicker Widget
| Property | Type | Description |
|---|---|---|
pathList |
Function(List<PickedAssetModel>) |
Required. Callback triggered on selection change. |
appBarLeadingWidget |
Widget? |
Optional widget at the leading position of the toolbar. |
mediaPickerParams |
MediaPickerParamsModel |
Required. Configuration model for all picker behavior & styling. |
MediaPickerParamsModel
| Parameter | Type | Default | Description |
|---|---|---|---|
appBarHeight |
double |
50.0 |
Height of the top AppBar. |
appBarColor |
Color? |
null |
Background color of the AppBar. Falls back to theme. |
maxPickImages |
int |
2 |
Maximum number of items allowed to select. |
singlePick |
bool |
true |
Whether only a single item can be picked. |
crossAxisCount |
int |
3 |
Number of columns in the grid. |
childAspectRatio |
double |
0.5 |
Width-to-height ratio of each grid item. |
mediaType |
GalleryMediaType |
.all |
Type of media to display (all / image / video / audio). |
thumbnailQuality |
ThumbnailQuality |
.medium |
Quality of generated thumbnails. |
thumbnailBoxFix |
BoxFit |
.cover |
How thumbnails fit within their boxes. |
thumbnailBgColor |
Color? |
null |
Background color of thumbnail containers. |
gridViewBgColor |
Color? |
null |
Background color of the entire grid view. |
gridPadding |
EdgeInsets? |
null |
Padding applied to the grid view. |
gridViewPhysics |
ScrollPhysics |
BouncingScrollPhysics() |
Scroll behavior of the grid. |
gridViewController |
ScrollController? |
null |
External scroll controller for the grid. |
albumTextColor |
Color? |
null |
Text color of the selected album name. |
albumDropDownBgColor |
Color? |
null |
Background color of the album dropdown overlay. |
albumSelectIconColor |
Color? |
null |
Icon color inside the album dropdown. |
albumSelectTextColor |
Color? |
null |
Text color inside the album dropdown. |
selectedAlbumTextColor |
Color? |
null |
Text color of the currently selected album. |
selectedAlbumIcon |
IconData |
Icons.check |
Icon shown next to the selected album. |
selectedAlbumBgColor |
Color? |
null |
Background of the selected album row. |
selectedAssetBgColor |
Color? |
null |
Overlay color applied to selected thumbnails. |
selectedCheckColor |
Color? |
null |
Color of the checkmark icon on selected items. |
selectedCheckBgColor |
Color? |
null |
Background color behind the selection checkmark. |
translations |
GalleryMediaPickerTranslations |
(defaults) |
Override all user-facing strings for i18n. |
GalleryMediaType
Controls which types of media assets are fetched from the device gallery.
| Value | Description |
|---|---|
all |
Show all media types (images, videos, GIFs, audio). |
image |
Show only image files (includes GIFs). |
video |
Show only video files. |
audio |
Show only audio files. |
ThumbnailQuality
Controls the resolution of native thumbnail generation. Higher quality provides more detail but may impact performance with very large galleries.
| Value | Pixel Size | Description |
|---|---|---|
low |
100×100 |
Fastest loading, lowest detail. |
medium |
250×250 |
Good balance for most use cases. |
high |
400×400 |
Best quality, recommended for full-screen pickers. |
PickedAssetModel
The data model returned for each selected asset via the pathList callback.
| Property | Type | Description |
|---|---|---|
id |
String |
Unique native identifier of the asset. |
path |
String |
File path to the asset on disk. |
type |
PickedAssetType |
Asset type: .image, .video, or .other. |
file |
File? |
File object, if available. |
title |
String? |
Original filename or title. |
width |
int |
Width in pixels. |
height |
int |
Height in pixels. |
size |
Size |
Original size. |
orientationWidth |
int |
Orientation-corrected width. |
orientationHeight |
int |
Orientation-corrected height. |
orientationSize |
Size |
Orientation-corrected size. |
videoDuration |
Duration |
Duration (zero for images). |
createDateTime |
DateTime |
Creation timestamp. |
modifiedDateTime |
DateTime |
Last modification timestamp. |
latitude |
double? |
GPS latitude, if available. |
longitude |
double? |
GPS longitude, if available. |
thumbnail |
Uint8List? |
Thumbnail bytes, if generated. |
GalleryMediaPickerTranslations
Override all user-facing strings for localization and accessibility.
| Property | Type | Default | Description |
|---|---|---|---|
noMediaFound |
String |
'No media found.' |
Shown when an album is empty. |
recent |
String |
'Recent' |
Label for the recents album. |
tapToOpenSettings |
String |
'Tap to open settings' |
Settings redirect label. |
permissionDenied |
String |
'Permissions denied...' |
Shown when gallery access is denied. |
maxItemsPicked |
String |
'You have already picked %s items.' |
Toast when selection limit is reached. Use %s for the number. |
videoLabel |
String |
'Video' |
Accessibility label for videos. |
imageLabel |
String |
'Image' |
Accessibility label for images. |
gifLabel |
String |
'GIF' |
Accessibility label for GIFs. |
selectedLabel |
String |
'Selected' |
Accessibility label for selected state. |
notSelectedLabel |
String |
'Not selected' |
Accessibility label for unselected state. |
⚡ Performance Architecture
The picker includes several performance optimizations built into its core:
O(1) Selection Rebuilds
Each thumbnail uses an internal _SelectionWatcher widget that caches its own selection boolean. When the global selection list changes, only thumbnails whose ownership actually flips trigger a setState. This means selecting one photo in a grid of 10,000 items rebuilds exactly 1 widget, not all visible items.
Persistent Thumbnail Caching with ValueKey
Each grid item is keyed with ValueKey(asset.id), ensuring Flutter's element tree correctly identifies and reuses cached images during rapid scrolling. This prevents the common "flicker on scroll back" issue where thumbnails would reload from disk.
Native Background Task Cancellation
When a user deselects a media item, the in-flight native file extraction (loadFile) is automatically cancelled via PMCancelToken.cancelRequest(), preventing wasted CPU/battery on files the user no longer wants.
Automatic Cache Cleanup
On dispose, PhotoManager.clearFileCache() is called to clean up temporary OS-generated cache files, preventing storage leaks especially on iOS where HEIC/HEVC transcoding creates temporary copies.
Race Condition Protection
An internal _albumChangeId counter prevents race conditions when rapidly switching between albums or media types, ensuring only the most recent request completes and updates the UI.
📹 Screenshots / Demo
Personalization
Dark Setting
Light Setting
Custom Setting
No leading widget
Album Selection
Pick single file
Pick multiple files
Select media type
Only videos
Only images and GIF
Quality comparison
Low
Medium
High
💬 Contributing
Pull requests are welcome! If you find bugs or have suggestions, feel free to open an issue.
📄 License
MIT License — see the LICENSE file for details.