File Picker
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');
}