flutter_photo_library 0.0.1
flutter_photo_library: ^0.0.1 copied to clipboard
High-performance photo and video manager for Flutter with native-speed gallery loading.
flutter_photo_library #
A Flutter plugin that exposes native photo-library APIs without UI.
Fetch albums, paginate images/videos, generate thumbnails, and resolve original files on Android and iOS — then build your own gallery UI.
Main highlight — fast media loading #
Designed for smooth gallery scrolling: paginated native fetches, thumbnail LRU caching, and lazy original loading so the UI stays responsive — no lag on scroll, no device hang, and faster media loading even with large libraries.
| Platforms | Android (minSdk 24), iOS (13.0+) |
| Dart | >=2.12.0 <4.0.0 |
| Flutter | >=2.0.0 |
| Version | 0.0.1 |
| License | MIT |
Demo #
Smooth gallery loading and browsing — fast media load with no lag or device hang:

Features #
| Feature | Description |
|---|---|
| Fast media loading | Main feature — paginated native queries + cached thumbnails for lag-free grids; no UI freeze or device hang on large libraries |
| Smooth scrolling | Loads pages and thumbs on demand so scrolling stays fluid |
| Local media fetch | Images and videos from device storage |
| Albums / folders | List non-empty albums; filter pages by albumId |
| Pagination | Page index starts at 0; each page returns up to 50 items by default |
| Thumbnails | Native generation + Dart LRU cache (max 400 entries) |
| Original access | Raw image bytes or video file URI via getOriginalFile |
| Media URL | Lazy local path/URI via getMediaUrl |
| Permissions | granted / denied / permanentlyDenied |
| No bundled UI | Data APIs only — you own the widgets |
Table of contents #
- Demo
- Install
- Configure native platforms
- Quick start
- Usage
- API reference
- Architecture
- Example app
- Limitations & notes
- License
Install #
dependencies:
flutter_photo_library: ^0.0.1
flutter pub add flutter_photo_library
import 'package:flutter_photo_library/flutter_photo_library.dart';
Configure native platforms #
Android #
The plugin merges these permissions into your app automatically:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
Runtime requests by API level:
| API | Permissions requested |
|---|---|
| 34+ | READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_VISUAL_USER_SELECTED (limited access) |
| 33 | READ_MEDIA_IMAGES, READ_MEDIA_VIDEO |
| ≤ 32 | READ_EXTERNAL_STORAGE |
To drop a permission you do not need (e.g. videos only / images only), remove it in your app AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission
android:name="android.permission.READ_MEDIA_VIDEO"
tools:node="remove" />
</manifest>
Use compileSdk 34+.
iOS #
Add to ios/Runner/Info.plist (required — missing this crashes on permission request):
<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to your photo library to display your images and videos.</string>
On iOS 14+, both full (.authorized) and limited (.limited) access count as granted.
Quick start #
import 'package:flutter_photo_library/flutter_photo_library.dart';
Future<void> loadGallery() async {
final status = await FlutterPhotoLibrary.requestPermissions();
if (status != PhotoLibraryPermissionStatus.granted) return;
final repo = FlutterPhotoLibraryRepository();
final albums = await repo.getAlbums(type: MediaFetchType.all);
final items = await repo.fetchMediaPage(
page: 0,
pageSize: 50,
fetchType: MediaFetchType.all,
albumId: albums.isNotEmpty ? albums.first.id : null,
);
final thumb = await repo.getThumbnail(
id: items.first.id,
type: items.first.type,
width: 200,
height: 200,
);
}
Usage #
1. Permissions #
Most APIs require access. Prefer checking first, then requesting if needed.
final bool alreadyGranted = await FlutterPhotoLibrary.checkPermissions();
if (!alreadyGranted) {
final status = await FlutterPhotoLibrary.requestPermissions();
switch (status) {
case PhotoLibraryPermissionStatus.granted:
break;
case PhotoLibraryPermissionStatus.permanentlyDenied:
// Open app Settings so the user can enable access.
break;
case PhotoLibraryPermissionStatus.denied:
// Denied for now; you can ask again later.
break;
}
}
2. Albums (MediaAlbum) #
final repository = FlutterPhotoLibraryRepository(); // singleton
final List<MediaAlbum> albums = await repository.getAlbums(
type: MediaFetchType.all, // .all | .image | .gif
);
// MediaAlbum → id, name, count
3. Paginated media (MediaItem) #
final List<MediaItem> items = await repository.fetchMediaPage(
page: 0, // 0-indexed
pageSize: FlutterPhotoLibraryRepository.defaultPageSize, // 50
fetchType: MediaFetchType.all,
albumId: selectedAlbum?.id, // null = all media
);
| Field | Notes |
|---|---|
id |
Native asset id (use with other APIs) |
uri |
Android: content://… · iOS: asset local identifier |
type |
MediaFetchType.image or .video |
duration |
Milliseconds (0 for images) |
width / height |
Pixel size |
dateAdded |
DateTime |
originalMediaUri |
Android: filesystem path from MediaStore when available. iOS: usually null — use getMediaUrl / getOriginalFile |
formattedDuration |
m:ss helper for videos |
4. Thumbnails #
Returns JPEG Uint8List? for Image.memory. Checks the in-memory LRU cache first, then native.
final Uint8List? bytes = await repository.getThumbnail(
id: item.id,
type: item.type, // default: MediaFetchType.image
width: 200, // physical px (default 200)
height: 200, // physical px (default 200)
);
Tips:
- Request physical pixels (
logicalSize * devicePixelRatio) for sharp grids. - Cache key is
id + width × height; different sizes are separate cache entries.
5. Media URL #
Resolve a local path/URI when you need a file location (especially on iOS, or when originalMediaUri is null):
final String? url = await repository.getMediaUrl(
id: item.id,
type: item.type,
);
- Android: typically a
content://URI - iOS: file URL; may download iCloud assets (
networkAccessAllowed)
6. Original file #
Prefer URIs for display/playback when possible. Use this when you need image bytes or a dedicated video URL:
final MediaFile? file = await repository.getOriginalFile(
id: item.id,
type: item.type,
);
if (file?.isImage == true) {
// file!.bytes → Uint8List (can be large)
} else if (file?.isVideo == true) {
// file!.videoUrl → local / content URI string
}
7. Clear Dart thumbnail cache #
repository.clearCache();
Useful on leave/refresh of a large gallery.
API reference #
FlutterPhotoLibrary (static) #
| Method | Returns | Description |
|---|---|---|
checkPermissions() |
Future<bool> |
Granted (incl. iOS limited / Android partial)? |
requestPermissions() |
Future<PhotoLibraryPermissionStatus> |
Shows system dialog when needed |
FlutterPhotoLibraryRepository (singleton) #
| Method | Returns | Description |
|---|---|---|
getAlbums({type}) |
Future<List<MediaAlbum>> |
Non-empty albums |
fetchMediaPage({page, pageSize, fetchType, albumId}) |
Future<List<MediaItem>> |
Paginated assets |
getThumbnail({id, type, width, height}) |
Future<Uint8List?> |
Thumbnail bytes (LRU) |
getMediaUrl({id, type}) |
Future<String?> |
Local path / content URI |
getOriginalFile({id, type}) |
Future<MediaFile?> |
Image bytes or video URI |
clearCache() |
void |
Clear Dart LRU thumbnail cache |
Constants: defaultPageSize = 50, defaultThumbnailWidth/Height = 200.
Enums #
enum MediaFetchType { all, image, video }
enum PhotoLibraryPermissionStatus {
granted,
denied,
permanentlyDenied,
}
Models #
class MediaAlbum {
final String id;
final String name;
final int count;
}
class MediaItem {
final String id;
final String uri;
final MediaFetchType type;
final int duration; // ms
final int width;
final int height;
final DateTime dateAdded;
final String? originalMediaUri;
String get formattedDuration;
}
class MediaFile {
final Uint8List? bytes; // image
final String? videoUrl; // gif
bool get isImage;
bool get isVideo;
}
Example app #
See example/ for a working gallery:
- Permission request + permanently-denied messaging
- Album dropdown + image / video / all filter
- Infinite paginated grid (
pageSize: 80in the sample) - Thumbnail cells with physical-pixel sizing
- Detail view via
getOriginalFile(image bytes / video URI +video_player)
cd example
flutter pub get # regenerates ios/Flutter/Generated.xcconfig for your machine
flutter run
Generated.xcconfig is gitignored on purpose (it embeds your local FLUTTER_ROOT). Always run flutter pub get after clone before pod install / Xcode.
License #
MIT © MOON TECHNOLABS — see LICENSE.