adaptive_media_picker 0.0.10
adaptive_media_picker: ^0.0.10 copied to clipboard
Adaptive Flutter media picker for images & videos with smart permissions, limited access UI, and cross-platform support (Android, iOS, Web, Desktop).
πΈ Adaptive Media Picker #
π Adaptive, permission-aware media picker for Flutter
Handles limited & full access gracefully β with native-like UX on Android, iOS, Web, and Desktop.
β¨ Why Adaptive Media Picker? #
Most media pickers only open the gallery or camera β but fail when permissions are limited or restricted.
adaptive_media_picker is designed to handle every case automatically, making your UX seamless.
π‘ What makes it different? #
- β Auto permission handling
- β Built-in limited-access sheet (for iOS & Android)
- β Optional image cropping (Android / iOS / Web)
- β Works seamlessly on Web, Desktop, and Mobile
- β Single unified API for images & videos
β¨ Built-in limited access bottom sheet UI (native full-access flow on Android/iOS)
π Features at a Glance #
| Feature | Description |
|---|---|
| π· Image & Video Picker | Pick single/multiple images or single videos |
| βοΈ Cropping | Optional crop (Android, iOS, Web) |
| π Permission-aware | Handles full, limited, denied states |
| π§ Cross-platform | Works on mobile, web, and desktop |
| πΌοΈ Built-in Limited Access UI | Native-like bottom sheet |
| π§© Fallbacks | Smart fallbacks for unsupported platforms |
| π― Web Safe | No dart:io β works on Flutter Web |
β οΈ Multiple video selection is not supported by native APIs.
ποΈ Platform Support Matrix #
| Feature | Android | iOS | Web | macOS | Windows | Linux |
|---|---|---|---|---|---|---|
| Single image pick | β | β | β | β | β | β |
| Multi-image pick | β | β | β | β | β | β |
| Single video pick | β | β | β | β | β | β |
| Multiple videos | β | β | β | β | β | β |
| Camera capture | β | β | β | β | β | β |
| Limited-access UX | β | β | β | β | β | β |
| Cropping (single image) | β | β | β | β | β | β |
β‘ Quick Start #
final picker = AdaptiveMediaPicker();
// Pick a single image
final singleImage = await picker.pickImage(
context: context,
options: const PickOptions(source: ImageSource.gallery, imageQuality: 80),
);
// Pick and crop
final croppedImage = await picker.pickImage(
context: context,
options: const PickOptions(source: ImageSource.gallery, wantToCrop: true),
);
// Pick multiple images
final multiImages = await picker.pickMultiImage(
context: context,
options: const PickOptions(maxImages: 5, source: ImageSource.gallery),
);
// Pick a single video
final singleVideo = await picker.pickVideo(
context: context,
options: const PickOptions(source: ImageSource.gallery),
);
π¨ Theming #
Both the built-in limited-access bottom sheet and the optional cropper can follow your app theme or be overridden via PickOptions:
- Automatic: By default, the package uses
Theme.of(context)for surfaces and text. - Override: Set these optional fields on
PickOptions:themeBrightness:Brightness.lightorBrightness.darkprimaryColor: the primary accent color (e.g.Colors.blue)
Example:
final result = await picker.pickImage(
context: context,
options: const PickOptions(
wantToCrop: true,
themeBrightness: Brightness.dark,
primaryColor: Colors.blue,
),
);
Notes:
- Limited-access bottom sheet inherits your app theme when overrides are not provided.
- Android cropper toolbar, controls, and grid/frame colors derive from
themeBrightnessandprimaryColor. - Web cropper uses the provided
context(dialog/page) and will follow your theme colors; primary accent applies to available UI elements.
π Common Use Cases #
- πΌοΈ Select & crop a profile picture
- πΈ Capture or choose multiple images for a gallery/post
- π₯ Pick single video from camera or gallery
- π Handle limited access permissions gracefully
βοΈ Cropping Setup #
Cropping is supported on Android, iOS, and Web.
π± Android #
Add UCropActivity to your AndroidManifest.xml:
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
β Android embedding v2 required
βΉοΈ If you enable cropping and build a release APK/AAB (R8/minify), UCrop may reference OkHttp classes. Add these dependencies to your appβs
build.gradle(.kts)to avoid missing-class errors (only needed when cropping on Android):
dependencies {
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.squareup.okio:okio:3.6.0")
}
π iOS #
No additional setup required.
π Web #
Add cropperjs to web/index.html:
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.2/cropper.css"
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.2/cropper.min.js"></script>
π Limited Access UX #
When the user grants limited access, the picker automatically shows a native-like dialog with options:
- π Manage Selection (iOS only)
- βοΈ Open Settings (iOS/macOS/Android)
- π Auto-dismisses after interaction
βοΈ Permissions Setup #
π§± Android #
<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_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
π iOS #
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photo library access to pick images.</string>
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to take photos and videos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access when recording videos.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app may save images/videos to your photo library.</string>
To ensure your app works smoothly with media picking and cropping on iOS, you'll need to configure the required permissions in your Podfile for Flutter.
Add the following code to the post_install section of your Podfile:
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
# Enable only the permissions you need in your app
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
'PERMISSION_CAMERA=1',
'PERMISSION_MICROPHONE=1',
'PERMISSION_PHOTOS=1',
# Examples of other permissions you might enable:
# 'PERMISSION_NOTIFICATIONS=1',
# 'PERMISSION_MEDIA_LIBRARY=1',
# 'PERMISSION_BLUETOOTH=1',
# 'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
]
end
end
end
This will make sure the necessary permissions for Camera, Microphone, and Photos are enabled in your iOS project, allowing the picker to function properly.
π» macOS #
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
π§© Desktop platforms use native file dialogs. Camera capture is not supported on desktop.
π§© API Overview #
| Method | Description |
|---|---|
pickImage() |
Pick single image (optionally cropped) |
pickMultiImage() |
Pick multiple images |
pickVideo() |
Pick single video |
π Data Models Overview #
π§© PickOptions #
Configuration options for image/video picking operations.
| Field | Type | Description |
|---|---|---|
maxImages |
int? |
Maximum number of images for multi-image pick. Ignored for single image/video. |
imageQuality |
int? |
JPEG compression quality (0β100). |
maxWidth |
int? |
Resize width for images when supported. |
maxHeight |
int? |
Resize height for images when supported. |
source |
ImageSource |
Source β gallery or camera. Falls back to gallery on web/desktop. |
showOpenSettingsDialog |
bool |
Show βOpen Settingsβ dialog when permission is permanently denied. |
settingsDialogTitle |
String? |
Custom title for the settings dialog. |
settingsDialogMessage |
String? |
Custom message for the settings dialog. |
settingsButtonLabel |
String? |
Label for the confirm button. |
cancelButtonLabel |
String? |
Label for the cancel button. |
wantToCrop |
bool |
Enable crop flow (Android/iOS/Web only, single image only). |
themeBrightness |
Brightness? |
Override theme for limited sheet & cropper (light/dark). |
primaryColor |
Color? |
Primary accent color for limited sheet & cropper (e.g., blue). |
logTag |
String? |
Optional debug tag for internal logging. |
πΌοΈ PickedMedia #
Represents a single picked image or video.
| Field | Type | Description |
|---|---|---|
path |
String |
Local file path to the picked media. |
mimeType |
String? |
MIME type if available. |
width |
int? |
Image width (when known). |
height |
int? |
Image height (when known). |
π§Ύ PickResultSingle #
Returned from pickImage() or pickVideo().
| Field | Type | Description |
|---|---|---|
item |
PickedMedia? |
Picked item, or null if none. |
permissionResolution |
PermissionResolution |
Final permission state after operation. |
metadata |
PickMetadata |
Metadata about crop and sizes. |
error |
PickError? |
Indicates if operation failed or canceled. |
π‘ Use
.isEmptyto check if no item was selected.
π§Ύ PickResultMultiple #
Returned from pickMultiImage().
| Field | Type | Description |
|---|---|---|
items |
List<PickedMedia> |
All picked images. Empty if none. |
permissionResolution |
PermissionResolution |
Final permission state. |
π‘ Use
.isEmptyto check if no images were selected.
π§ PickMetadata #
Extra info for debugging and analytics.
| Field | Type | Description |
|---|---|---|
cropApplied |
bool |
Whether cropping was applied. |
originalSize |
Size? |
Size before transformations. |
finalSize |
Size? |
Size after transformations. |
β οΈ PickError #
Typed error codes for single-pick operations.
| Value | Description |
|---|---|
canceled |
User canceled selection. |
cropCanceled |
User canceled cropping. |
io |
I/O or platform failure. |
unknown |
Unknown reason. |
π PermissionResolution #
Represents the final permission outcome.
| Field | Type | Description |
|---|---|---|
granted |
bool |
True if any form of access was granted. |
limited |
bool |
True if access is limited (iOS/Android 14+). |
permanentlyDenied |
bool |
True if user must change settings manually. |
Factories
| Factory | Description |
|---|---|
PermissionResolution.grantedFull() |
Full access granted. |
PermissionResolution.grantedLimited() |
Limited access granted. |
PermissionResolution.denied() |
Access denied (optionally permanent). |
π€ Author #
Created with β€οΈ by Jaimin Kavathia - πΌ LinkedIn
π License #
Licensed under the MIT License. Free for personal & commercial use.
β If you like this package, give it a star on GitHub & pub.flutter-io.cn!