media_picker_plus 1.0.0
media_picker_plus: ^1.0.0 copied to clipboard
A Flutter plugin to pick or capture images and videos with quality control options
Media Picker Plus #
A comprehensive Flutter plugin for media selection with advanced processing capabilities. Pick images, videos, and files from gallery or camera with built-in watermarking, resizing, and quality control features.
💡 Who is this for? #
This plugin is ideal for developers building:
- Social media apps (like Instagram, TikTok clones)
- Chat/messaging apps with media features
- Document scanners or photo editing apps
- Any app requiring rich media capture and processing
🚀 Features #
- Media Selection: Pick images and videos from gallery or capture using camera
- File Picking: Select files with extension filtering
- Multiple Selection: Pick multiple images, videos, or files at once
- Advanced Processing:
- Image resizing with aspect ratio preservation
- Media cropping with aspect ratio control and freeform options
- Interactive cropping UI for manual crop selection
- Quality control for images and videos
- Watermarking with customizable position and font size
- Permission Management: Smart permission handling for camera and gallery access
- Cross-Platform: Full support for Android, iOS, macOS, and Web
- FFmpeg Integration: Advanced video processing capabilities
✅ Platform Support #
⚠️ Web Developers: If you're targeting web, please read the Web Platform Considerations section in the Usage guide to avoid common runtime errors with
PlatformAPIs,Image.file(), andVideoPlayerController.file().
| Feature | Android | iOS | Web | macOS |
|---|---|---|---|---|
| Pick image | ✅ | ✅ | ✅ | ✅ |
| Capture image | ✅ | ✅ | ✅ | ✅ |
| Crop image | ✅ | ✅ | ✅ | ✅ |
| Resize image | ✅ | ✅ | ✅ | ✅ |
| Watermark image | ✅ | ✅ | ✅ | ✅ |
| Pick video | ✅ | ✅ | ✅ | ✅ |
| Capture video | ✅ | ✅ | ✅ | ✅ |
| Watermark video | ✅ | ✅ | ⚠️* | ✅ |
* Video watermarking on web requires optional FFmpeg.js setup. See docs/web-permissions.md for details.
📋 Requirements #
Flutter Environment #
- Flutter SDK:
>= 2.5.0 - Dart SDK:
>= 2.17.0 < 4.0.0
Platform Requirements #
Android
- Supported Versions: Android 5.1 - Android 14+ (API 22 - API 34+)
- Minimum SDK: API 21 (Android 5.0)
- Compile SDK: API 34+ (recommended)
- Target SDK: API 34+ (recommended)
iOS
- Supported Versions: iOS 11.0 - iOS 17+
- Minimum Version: iOS 11.0
- Xcode: 12.0+
macOS
- Supported Versions: macOS 11.0 - macOS 14+
- Minimum Version: macOS 11.0
- Xcode: 12.0+
Web
- Requirements: Modern browsers with HTML5 support
- Features: WebRTC for camera, File API for uploads
- Browser Support: Chrome 63+, Firefox 65+, Safari 11.1+, Edge 79+
🔧 Installation #
Add this to your pubspec.yaml:
dependencies:
media_picker_plus: ^0.0.1
Or install via command line:
flutter pub add media_picker_plus
⚙️ Platform Setup #
Android Configuration #
- Add permissions to
android/app/src/main/AndroidManifest.xml:
The plugin requires different permissions based on the features you use and the Android API levels you support:
<!-- Required for camera capture -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- Required for video recording -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Storage permissions for Android 6.0 - 12 (API 23-32) -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!-- Granular media permissions for Android 13+ (API 33+) -->
<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" />
<!-- Optional: Feature declarations for better Play Store filtering -->
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.microphone" android:required="false" />
Permission Usage Guidelines:
- Only add permissions for features you actually use
CAMERApermission is required for photo/video captureRECORD_AUDIOpermission is required for video recording with audio- Storage permissions are required for gallery access
- For Android 13+ (API 33+), use granular media permissions instead of
READ_EXTERNAL_STORAGE
- Configure FileProvider in
android/app/src/main/AndroidManifest.xml:
<application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
- Create
android/app/src/main/res/xml/file_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="my_images" path="Pictures" />
</paths>
Detailed API Level Compatibility:
| Android Version | API Level | Permission Model | Required Permissions |
|---|---|---|---|
| Android 5.0-5.1 | 21-22 | Install-time | All permissions granted at install |
| Android 6.0-8.1 | 23-27 | Runtime | READ_EXTERNAL_STORAGE, CAMERA, RECORD_AUDIO |
| Android 9 | 28 | Runtime | READ_EXTERNAL_STORAGE, CAMERA, RECORD_AUDIO |
| Android 10 | 29 | Runtime + Scoped Storage | READ_EXTERNAL_STORAGE, CAMERA, RECORD_AUDIO |
| Android 11 | 30 | Runtime + Scoped Storage | READ_EXTERNAL_STORAGE, CAMERA, RECORD_AUDIO |
| Android 12 | 31-32 | Runtime + Scoped Storage | READ_EXTERNAL_STORAGE, CAMERA, RECORD_AUDIO |
| Android 13+ | 33+ | Runtime + Granular Media | READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO, CAMERA, RECORD_AUDIO |
Key Changes by Version:
- API 23+: Runtime permissions introduced
- API 29+: Scoped storage, app-specific directories preferred
- API 33+: Granular media permissions, modern Photo Picker API
- API 34+: Enhanced Photo Picker features
Feature Availability:
- Modern Photo Picker: Android 13+ (API 33+) - automatically used when available
- Legacy Gallery Picker: All versions - fallback for older Android versions
- Camera Capture: All versions
- Video Recording: All versions
- File Picking: All versions
iOS Configuration #
Supported iOS Versions: 11.0 - 17+
- Add permissions to
ios/Runner/Info.plist:
<!-- Required for camera capture (all iOS versions) -->
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to take photos and videos.</string>
<!-- Required for photo library access (all iOS versions) -->
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photo library access to select images and videos.</string>
<!-- Required for video recording with audio (all iOS versions) -->
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access to record videos with audio.</string>
<!-- Optional: Required for adding photos to library (if you implement save functionality) -->
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app needs permission to save photos to your photo library.</string>
- Set minimum iOS version in
ios/Podfile:
platform :ios, '11.0'
- Configure deployment target in
ios/Runner.xcodeproj:- Open
ios/Runner.xcodeprojin Xcode - Select "Runner" target
- Set "iOS Deployment Target" to "11.0" or higher
- Open
iOS Version Compatibility:
| iOS Version | Features Available | Notes |
|---|---|---|
| iOS 11.0-12.x | Basic camera, photo library access | Core functionality |
| iOS 13.0+ | Enhanced camera features, improved permission handling | Better user experience |
| iOS 14.0+ | Limited photo library access, improved privacy | PHPickerViewController support |
| iOS 15.0+ | Enhanced photo picker, camera improvements | Optimized performance |
| iOS 16.0+ | Camera improvements, better privacy controls | Latest features |
| iOS 17.0+ | Advanced camera features, enhanced privacy | Cutting-edge support |
Permission Behavior by iOS Version:
- iOS 11.0-13.x: Traditional permission model
- iOS 14.0+: Limited photo library access by default
- iOS 14.0+: PHPickerViewController available (automatically used when beneficial)
- iOS 15.0+: Enhanced privacy controls
- iOS 16.0+: Improved camera authorization states
macOS Configuration #
Supported macOS Versions: 11.0 - 14+
- Add permissions to
macos/Runner/Info.plist:
<!-- Required for camera capture (all macOS versions) -->
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to take photos and videos.</string>
<!-- Required for microphone access during video recording (all macOS versions) -->
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access to record videos with audio.</string>
<!-- Required for photo library access (macOS 11.0+) -->
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photo library access to select images and videos.</string>
<!-- Optional: Required for file system access beyond user-selected files -->
<key>NSDesktopFolderUsageDescription</key>
<string>This app needs access to the Desktop folder to save media files.</string>
<key>NSDocumentsFolderUsageDescription</key>
<string>This app needs access to the Documents folder to save media files.</string>
<key>NSDownloadsFolderUsageDescription</key>
<string>This app needs access to the Downloads folder to save media files.</string>
- Set minimum macOS version in
macos/Podfile:
platform :osx, '11.0'
-
Configure deployment target in
macos/Runner.xcodeproj:- Open
macos/Runner.xcodeprojin Xcode - Select "Runner" target
- Set "macOS Deployment Target" to "11.0" or higher
- Open
-
Enable necessary capabilities in
macos/Runner/Runner.entitlements:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Required for camera access -->
<key>com.apple.security.device.camera</key>
<true/>
<!-- Required for microphone access -->
<key>com.apple.security.device.microphone</key>
<true/>
<!-- Required for file system access -->
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<!-- Optional: Required for broader file system access -->
<key>com.apple.security.files.downloads.read-write</key>
<true/>
</dict>
</plist>
macOS Version Compatibility:
| macOS Version | Features Available | Notes |
|---|---|---|
| macOS 11.0 Big Sur | Core functionality, basic permissions | Minimum supported version |
| macOS 12.0 Monterey | Enhanced camera support, improved file access | Better performance |
| macOS 13.0 Ventura | Advanced camera features, refined permissions | Enhanced user experience |
| macOS 14.0 Sonoma | Continuity Camera support, latest features | Cutting-edge support |
Permission Behavior by macOS Version:
- macOS 11.0+: Modern permission system, app sandboxing
- macOS 12.0+: Enhanced camera and microphone permission handling
- macOS 13.0+: Improved file system access controls
- macOS 14.0+: Continuity Camera support, enhanced privacy controls
File Access Methods:
- User-Selected Files: No special permissions required (NSOpenPanel)
- App-Specific Directories: Automatic access to app sandbox directories
- System Directories: Requires explicit entitlements and user permission
- Photo Library: Requires NSPhotoLibraryUsageDescription for programmatic access
Web Configuration #
Supported Browsers and Versions:
| Browser | Minimum Version | Camera Support | File API Support | Notes |
|---|---|---|---|---|
| Chrome | 63+ | ✅ Full | ✅ Full | Best performance |
| Firefox | 65+ | ✅ Full | ✅ Full | Excellent support |
| Safari | 11.1+ | ✅ Full | ✅ Full | iOS Safari 11.1+ |
| Edge | 79+ | ✅ Full | ✅ Full | Chromium-based |
| Opera | 50+ | ✅ Full | ✅ Full | Good support |
No additional configuration required for basic functionality.
Optional Configuration for Enhanced Features:
-
Enable HTTPS for camera access (required for camera in production):
- Camera access requires HTTPS in production
- Use
flutter run --web-port 8080 --web-hostname localhostfor local testing
-
Configure Content Security Policy (CSP) if using strict CSP:
<!-- Add to web/index.html <head> section -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
media-src 'self' blob:;
img-src 'self' data: blob:;
script-src 'self' 'unsafe-inline';">
- Web App Manifest for PWA features (optional):
{
"name": "Media Picker Plus App",
"short_name": "MediaPicker",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#000000",
"permissions": [
"camera",
"microphone"
]
}
Browser Feature Support:
| Feature | Chrome | Firefox | Safari | Edge | Notes |
|---|---|---|---|---|---|
| Camera Capture | ✅ | ✅ | ✅ | ✅ | Requires HTTPS in production |
| Video Recording | ✅ | ✅ | ✅ | ✅ | MediaRecorder API |
| File Upload | ✅ | ✅ | ✅ | ✅ | HTML5 File API |
| Multiple Selection | ✅ | ✅ | ✅ | ✅ | Native browser support |
| Image Processing | ✅ | ✅ | ✅ | ✅ | Canvas API |
| Watermarking | ✅ | ✅ | ✅ | ✅ | Client-side processing |
| Cropping | ✅ | ✅ | ✅ | ✅ | Canvas-based |
Web Limitations:
- File system access is limited to user-selected files
- Camera access requires user interaction
- Some advanced video processing features may be limited
- Performance depends on browser and device capabilities
Development vs Production:
- Development: HTTP localhost allowed for camera access
- Production: HTTPS required for camera and microphone access
- Mobile browsers: May have different permission behaviors
📖 Usage #
Import the package #
import 'package:media_picker_plus/media_picker_plus.dart';
⚠️ Important: Web Platform Considerations #
If your app targets web, read this section carefully to avoid runtime errors.
The Problem
On web, many Flutter APIs that work on mobile/desktop will throw errors:
- ❌
Platform.isAndroid,Platform.isIOS→ "Unsupported operation: Platform._operatingSystem" - ❌
Image.file()→ "Image.file is not supported on Flutter Web" - ❌
VideoPlayerController.file()→ Runtime error - ❌
Permission.photos,Permission.camerafrompermission_handler→ Not supported
The Solution
Always check kIsWeb BEFORE using platform-specific code:
import 'dart:io';
import 'package:flutter/foundation.dart'; // For kIsWeb
// ✅ CORRECT: Check kIsWeb first
if (kIsWeb) {
// Use web-specific code
} else if (Platform.isAndroid) {
// Use Android-specific code
}
// ❌ WRONG: Will crash on web
if (Platform.isAndroid) { // This line crashes on web!
// ...
}
Displaying Images (Web vs Native)
On web, media_picker_plus returns data URLs or blob URLs. You must use Image.network():
import 'dart:io';
import 'package:flutter/foundation.dart';
Widget _buildImage(String path, BoxFit fit) {
if (kIsWeb || path.startsWith('data:') || path.startsWith('blob:')) {
// Web: Use Image.network for URLs
return Image.network(path, fit: fit);
} else {
// Native: Use Image.file for file paths
return Image.file(File(path), fit: fit);
}
}
// Usage:
String? imagePath = await MediaPickerPlus.pickImage();
if (imagePath != null) {
_buildImage(imagePath, BoxFit.contain);
}
Playing Videos (Web vs Native)
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:video_player/video_player.dart';
void _setVideo(String path) {
_videoController?.dispose();
if (kIsWeb || path.startsWith('data:') || path.startsWith('blob:')) {
// Web: Use network controller for URLs
_videoController = VideoPlayerController.networkUrl(Uri.parse(path));
} else {
// Native: Use file controller for file paths
_videoController = VideoPlayerController.file(File(path));
}
_videoController!.initialize().then((_) {
setState(() {
_videoController!.play();
});
});
}
Permission Handling (Web vs Native)
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:permission_handler/permission_handler.dart';
Future<void> _requestPermissions() async {
// Skip permission requests on web and desktop
// Web: Browser handles permissions automatically
// Desktop: Configured via Info.plist/manifest
if (kIsWeb || Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
return;
}
// Only request on mobile (Android/iOS)
await [
Permission.camera,
Permission.microphone,
Permission.photos,
Permission.storage,
].request();
}
Key Takeaways for Web
- Always check
kIsWebfirst before usingPlatformAPIs - Use
Image.network()for images on web (data URLs) - Use
VideoPlayerController.networkUrl()for videos on web - Skip permission requests on web - browser handles them automatically
- Video watermarks are optional on web (requires FFmpeg.js setup)
See full web platform guide: docs/web-permissions.md
Permission Handling #
Before using any media picking functionality, it's recommended to check and request permissions:
// Check camera permission
bool hasCameraPermission = await MediaPickerPlus.hasCameraPermission();
if (!hasCameraPermission) {
bool granted = await MediaPickerPlus.requestCameraPermission();
if (!granted) {
// Handle permission denied
return;
}
}
// Check gallery permission
bool hasGalleryPermission = await MediaPickerPlus.hasGalleryPermission();
if (!hasGalleryPermission) {
bool granted = await MediaPickerPlus.requestGalleryPermission();
if (!granted) {
// Handle permission denied
return;
}
}
Basic Image Picking #
Pick Image from Gallery
// Simple image picking
String? imagePath = await MediaPickerPlus.pickImage();
// With custom options
String? imagePath = await MediaPickerPlus.pickImage(
options: const MediaOptions(
imageQuality: 90,
maxWidth: 1920,
maxHeight: 1080,
),
);
Capture Photo with Camera
// Simple photo capture
String? imagePath = await MediaPickerPlus.capturePhoto();
// With custom options
String? imagePath = await MediaPickerPlus.capturePhoto(
options: const MediaOptions(
imageQuality: 85,
maxWidth: 1600,
maxHeight: 1200,
),
);
Basic Video Handling #
Pick Video from Gallery
// Simple video picking
String? videoPath = await MediaPickerPlus.pickVideo();
// With duration limit
String? videoPath = await MediaPickerPlus.pickVideo(
options: const MediaOptions(
maxDuration: Duration(minutes: 5),
),
);
Record Video with Camera
// Simple video recording
String? videoPath = await MediaPickerPlus.recordVideo();
// With custom options
String? videoPath = await MediaPickerPlus.recordVideo(
options: const MediaOptions(
maxDuration: Duration(minutes: 2),
),
);
Multiple Media Selection #
Pick Multiple Images
List<String>? imagePaths = await MediaPickerPlus.pickMultipleImages();
// With custom options
List<String>? imagePaths = await MediaPickerPlus.pickMultipleImages(
options: const MediaOptions(
imageQuality: 80,
maxWidth: 1280,
maxHeight: 1280,
),
);
Pick Multiple Videos
List<String>? videoPaths = await MediaPickerPlus.pickMultipleVideos();
// With duration limits
List<String>? videoPaths = await MediaPickerPlus.pickMultipleVideos(
options: const MediaOptions(
maxDuration: Duration(minutes: 3),
),
);
File Picking #
Pick Single File
// Pick any file
String? filePath = await MediaPickerPlus.pickFile();
// Pick specific file types
String? pdfPath = await MediaPickerPlus.pickFile(
allowedExtensions: ['pdf', 'doc', 'docx'],
);
Pick Multiple Files
// Pick multiple files
List<String>? filePaths = await MediaPickerPlus.pickMultipleFiles();
// Pick multiple files with specific extensions
List<String>? imagePaths = await MediaPickerPlus.pickMultipleFiles(
allowedExtensions: ['jpg', 'png', 'gif', 'bmp'],
);
Advanced Image Cropping #
Interactive Cropping
For interactive cropping with a built-in UI, provide a BuildContext:
// Enable interactive cropping with square aspect ratio
String? croppedImage = await MediaPickerPlus.pickImage(
context: context,
options: MediaOptions(
cropOptions: CropOptions.square, // 1:1 aspect ratio
),
);
// Enable interactive cropping with custom aspect ratio
String? croppedImage = await MediaPickerPlus.capturePhoto(
context: context,
options: const MediaOptions(
cropOptions: CropOptions(
enableCrop: true,
aspectRatio: 16.0 / 9.0, // Widescreen
lockAspectRatio: true,
showGrid: true,
),
),
);
// Freeform cropping (no aspect ratio constraints)
String? croppedImage = await MediaPickerPlus.pickImage(
context: context,
options: const MediaOptions(
cropOptions: CropOptions(
enableCrop: true,
freeform: true,
showGrid: true,
),
),
);
Pre-defined Crop Ratios
// Square crop (1:1)
String? squareImage = await MediaPickerPlus.pickImage(
context: context,
options: const MediaOptions(cropOptions: CropOptions.square),
);
// Portrait crop (3:4)
String? portraitImage = await MediaPickerPlus.pickImage(
context: context,
options: const MediaOptions(cropOptions: CropOptions.portrait),
);
// Landscape crop (4:3)
String? landscapeImage = await MediaPickerPlus.pickImage(
context: context,
options: const MediaOptions(cropOptions: CropOptions.landscape),
);
// Widescreen crop (16:9)
String? widescreenImage = await MediaPickerPlus.pickImage(
context: context,
options: const MediaOptions(cropOptions: CropOptions.widescreen),
);
Programmatic Cropping
// Define crop area manually (values are normalized 0.0-1.0)
String? croppedImage = await MediaPickerPlus.pickImage(
options: const MediaOptions(
cropOptions: CropOptions(
enableCrop: true,
cropRect: CropRect(
x: 0.1, // 10% from left
y: 0.1, // 10% from top
width: 0.8, // 80% of image width
height: 0.8, // 80% of image height
),
),
),
);
Watermarking #
Image Watermarking
// Add watermark during image picking
String? watermarkedImage = await MediaPickerPlus.pickImage(
options: const MediaOptions(
watermark: "© 2024 MyApp",
watermarkPosition: WatermarkPosition.bottomRight,
watermarkFontSizePercentage: 5.0, // 5% of shorter edge
),
);
// Add watermark to existing image
String? watermarkedImage = await MediaPickerPlus.addWatermarkToImage(
existingImagePath,
options: const MediaOptions(
watermark: "© 2024 MyApp",
watermarkPosition: WatermarkPosition.bottomLeft,
watermarkFontSize: 24.0, // Fixed font size
),
);
Video Watermarking
// Add watermark during video recording
String? watermarkedVideo = await MediaPickerPlus.recordVideo(
options: const MediaOptions(
watermark: "© 2024 MyApp",
watermarkPosition: WatermarkPosition.topRight,
watermarkFontSizePercentage: 3.0,
),
);
// Add watermark to existing video
String? watermarkedVideo = await MediaPickerPlus.addWatermarkToVideo(
existingVideoPath,
options: const MediaOptions(
watermark: "© 2024 MyApp",
watermarkPosition: WatermarkPosition.bottomCenter,
watermarkFontSizePercentage: 4.0,
),
);
Watermark Positions
// Available watermark positions
WatermarkPosition.topLeft
WatermarkPosition.topCenter
WatermarkPosition.topRight
WatermarkPosition.middleLeft
WatermarkPosition.middleCenter
WatermarkPosition.middleRight
WatermarkPosition.bottomLeft
WatermarkPosition.bottomCenter
WatermarkPosition.bottomRight
Complete MediaOptions Configuration #
const MediaOptions(
// Image quality (0-100, higher = better quality)
imageQuality: 90,
// Maximum dimensions (pixels)
maxWidth: 1920,
maxHeight: 1080,
// Video duration limit
maxDuration: Duration(minutes: 5),
// Watermark settings
watermark: "© 2024 MyApp",
watermarkPosition: WatermarkPosition.bottomRight,
// Font size options (use one or the other)
watermarkFontSize: 24.0, // Fixed pixel size
watermarkFontSizePercentage: 4.0, // Percentage of shorter edge
// Cropping options
cropOptions: CropOptions(
enableCrop: true,
aspectRatio: 16.0 / 9.0,
lockAspectRatio: true,
freeform: false,
showGrid: true,
cropRect: CropRect(x: 0.1, y: 0.1, width: 0.8, height: 0.8),
),
)
Error Handling #
try {
String? imagePath = await MediaPickerPlus.pickImage();
if (imagePath != null) {
// Process the selected image
print('Image selected: $imagePath');
} else {
// User cancelled the picker
print('No image selected');
}
} catch (e) {
// Handle errors (permission denied, camera not available, etc.)
print('Error picking image: $e');
}
Complete Example #
import 'package:flutter/material.dart';
import 'package:media_picker_plus/media_picker_plus.dart';
class MediaPickerExample extends StatefulWidget {
@override
_MediaPickerExampleState createState() => _MediaPickerExampleState();
}
class _MediaPickerExampleState extends State<MediaPickerExample> {
String? _selectedImagePath;
Future<void> _pickImageWithCropping() async {
try {
// Check permissions first
bool hasPermission = await MediaPickerPlus.hasGalleryPermission();
if (!hasPermission) {
bool granted = await MediaPickerPlus.requestGalleryPermission();
if (!granted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Gallery permission denied')),
);
return;
}
}
// Pick image with interactive cropping and watermark
String? imagePath = await MediaPickerPlus.pickImage(
context: context,
options: const MediaOptions(
imageQuality: 90,
maxWidth: 1920,
maxHeight: 1080,
watermark: "© 2024 MyApp",
watermarkPosition: WatermarkPosition.bottomRight,
watermarkFontSizePercentage: 4.0,
cropOptions: CropOptions(
enableCrop: true,
freeform: true,
showGrid: true,
),
),
);
if (imagePath != null) {
setState(() {
_selectedImagePath = imagePath;
});
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Media Picker Plus Example'),
),
body: Column(
children: [
ElevatedButton(
onPressed: _pickImageWithCropping,
child: Text('Pick & Crop Image'),
),
if (_selectedImagePath != null)
Expanded(
child: Image.file(File(_selectedImagePath!)),
),
],
),
);
}
}
Tips and Best Practices #
- Always check permissions before using camera or gallery features
- Use appropriate image quality settings to balance file size and quality
- Set reasonable maximum dimensions to prevent memory issues
- Provide user feedback during media processing operations
- Handle null returns gracefully (user cancellation)
- Use percentage-based watermark font sizes for consistent appearance across different media sizes
- Test interactive cropping UI on different screen sizes and orientations
🔐 Privacy and Security #
Data Handling #
- All media processing happens locally on the device
- No data is transmitted to external servers
- Temporary files are automatically cleaned up
- Watermarking is applied offline
Permissions #
- Requests minimal necessary permissions
- Graceful handling of permission denials
- Clear usage descriptions for all permissions
🐛 Troubleshooting #
Common Issues #
Android Build Issues:
- Ensure
minSdkVersionis at least 21 - Check FileProvider configuration
- Verify permissions in AndroidManifest.xml
iOS Build Issues:
- Ensure iOS deployment target is 11.0+
- Check Info.plist permission descriptions
- Verify Podfile configuration
Permission Issues:
- Always check permissions before using features
- Handle permission denials gracefully
- Provide clear user feedback
Performance Optimization #
- Use appropriate image quality settings
- Set reasonable max dimensions
- Consider file size for watermarking
- Implement progress indicators for long operations
📄 License #
This project is licensed under the MIT License - see the LICENSE file for details.
🤝 Contributing #
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
🌟 Support #
If you find this plugin helpful, please consider:
- ⭐ Starring the repository
- 🐛 Reporting bugs
- 💡 Suggesting new features
- 📝 Contributing to documentation
For support, please open an issue on GitHub.
Made with ❤️ by thanhtunguet