File Picker
The Pick facade provides a unified interface for accessing the device camera, media gallery, and file system, returning a consistent MagicFile object regardless of platform.
- Introduction
- Picking Images
- Picking Videos
- Picking Files
- MagicFile Reference
- Complete Examples
- Upgrading to file_picker v11
Introduction
The Pick facade provides a Laravel-style interface for accessing the device's camera and file system. It handles platform-specific implementations and permissions, returning a unified MagicFile object.
Underlying packages:
image_pickerfor images and camerafile_pickerfor documents and directories
Picking Images
From Gallery
// Single image
final MagicFile? image = await Pick.image(
maxWidth: 1024,
maxHeight: 1024,
imageQuality: 85, // 0-100
);
// Multiple images
final List images = await Pick.images();
From Camera
final MagicFile? photo = await Pick.camera(
preferredCamera: CameraDevice.front, // or .rear
maxWidth: 800,
imageQuality: 90,
fallbackToGallery: true, // If camera permission denied
onError: (e) => print('Camera error: $e'),
);
Picking Videos
From Gallery
final MagicFile? video = await Pick.video(
maxDuration: Duration(minutes: 5),
);
Record New Video
final MagicFile? video = await Pick.recordVideo(
maxDuration: Duration(seconds: 30),
preferredCamera: CameraDevice.rear,
);
Picking Files
Any File
final MagicFile? file = await Pick.file();
Filtered by Extension
final MagicFile? pdf = await Pick.file(extensions: ['pdf']);
final MagicFile? doc = await Pick.file(extensions: ['doc', 'docx']);
Multiple Files
final List files = await Pick.files(
extensions: ['jpg', 'png', 'pdf'],
);
Directory (Mobile/Desktop)
final String? directoryPath = await Pick.directory();
MagicFile Reference
All Pick methods return MagicFile (or List). This class provides unified access to file data and convenient methods for storage and upload.
Properties
| Property | Type | Description |
|---|---|---|
path |
String? |
Original file path (null on Web) |
name |
String |
File name with extension (e.g., 'photo.jpg') |
size |
int? |
File size in bytes |
mimeType |
String? |
MIME type (e.g., 'image/jpeg') |
extension |
String |
Extension without dot (e.g., 'jpg') |
isImage |
bool |
True for: jpg, jpeg, png, gif, webp, bmp, heic |
isVideo |
bool |
True for: mp4, mov, avi, mkv, webm, m4v |
Example:
final file = await Pick.image();
if (file != null) {
print(file.name); // 'IMG_001.jpg'
print(file.extension); // 'jpg'
print(file.mimeType); // 'image/jpeg'
print(file.size); // 245760
print(file.isImage); // true
print(file.isVideo); // false
}
Methods
readAsBytes()
Read file as byte array. Bytes are cached after first read.
final Uint8List? bytes = await file.readAsBytes();
store(path, {disk})
Save to Magic Storage:
final storedPath = await file.store('avatars/profile.jpg');
// Returns: 'avatars/profile.jpg'
// To specific disk
await file.store('avatars/profile.jpg', disk: 'public');
storeAs(directory, {disk})
Save with auto-generated unique filename:
final storedPath = await file.storeAs('uploads');
// Returns: 'uploads/1704067200000_IMG_001.jpg'
upload(url, {fieldName, data, headers})
Upload to server via multipart form:
final response = await file.upload(
'/api/upload',
fieldName: 'document', // Form field name (default: 'file')
data: {
'user_id': user.id,
'category': 'profile',
},
headers: {
'X-Custom-Header': 'value',
},
);
if (response.successful) {
final uploadedUrl = response['url'];
Magic.success('Uploaded!', uploadedUrl);
} else {
Magic.error('Upload failed', response['message']);
}
Complete Examples
Profile Picture Upload
Future updateAvatar() async {
final image = await Pick.image(maxWidth: 512, imageQuality: 80);
if (image == null) return;
Magic.loading(message: 'Uploading...');
final response = await image.upload('/api/user/avatar');
Magic.closeLoading();
if (response.successful) {
Magic.success('Success', 'Avatar updated!');
user.avatarUrl = response['url'];
} else {
Magic.error('Error', response['message'] ?? 'Upload failed');
}
}
Document Picker with Local Storage
Future saveDocument() async {
final doc = await Pick.file(extensions: ['pdf', 'doc', 'docx']);
if (doc == null) return;
// Store locally
final path = await doc.storeAs('documents');
// Save reference
await Document.create({
'name': doc.name,
'path': path,
'size': doc.size,
'mime_type': doc.mimeType,
});
Magic.success('Saved', doc.name);
}
Gallery with Multiple Selection
Future uploadGallery() async {
final images = await Pick.images();
if (images.isEmpty) return;
Magic.loading(message: 'Uploading ${images.length} images...');
for (final image in images) {
await image.upload('/api/gallery', data: {
'album_id': currentAlbum.id,
});
}
Magic.closeLoading();
Magic.success('Done', '${images.length} images uploaded');
}
Upgrading to file_picker v11
Magic uses file_picker ^11.0.2. If you are migrating a project that pinned an older version, this section describes the breaking changes.
Static API (breaking change)
file_picker v11 removed the FilePicker.platform instance accessor. All methods are now called directly on the FilePicker class as static methods.
Before (v10 and earlier):
// Old pattern, no longer compiles in v11
final result = await FilePicker.platform.pickFiles();
final path = await FilePicker.platform.getDirectoryPath();
After (v11):
// New static API
final result = await FilePicker.pickFiles();
final path = await FilePicker.getDirectoryPath();
The Pick facade wraps file_picker entirely behind its own static methods, so if you use Pick.file(), Pick.files(), Pick.directory(), and Pick.saveFile() exclusively, this change is transparent and requires no action on your part.
If your code calls FilePicker.platform directly (bypassing the Pick facade), you must remove .platform from every call site.
Android path traversal security fix (CWE-22)
file_picker v11.0.2 patches a path traversal vulnerability (CWE-22) on Android. The vulnerability allowed a malicious file name returned by a document provider to traverse outside the intended directory. Upgrading ensures returned file paths are sanitized before they reach your application code.
No API changes are required on your side to benefit from this fix.
WASM web support
file_picker v11 adds support for Flutter Web compiled to WebAssembly (WASM). If you target flutter build web --wasm, file picking now works in that build mode without additional configuration.