file_picker_next
A Flutter plugin for picking files that fully complies with Google Play's Photo and Video Permissions policy.
🎯 Why file_picker_next?
Google Play now rejects apps that request READ_MEDIA_IMAGES or READ_MEDIA_VIDEO permissions for one-time file access. This plugin solves that problem by using:
- Android: Storage Access Framework (SAF) + Android Photo Picker - NO permissions required ✅
- iOS: UIDocumentPickerViewController + Native Photo Picker - NO extra permissions ✅
✨ Features
- ✅ Zero storage permissions on Android - fully Play Store compliant
- 📁 Pick single or multiple files
- 🖼️ Support for images, videos, documents, and custom file types
- 💾 Save files to Downloads (Android) / Documents (iOS)
- 📷 Camera integration via image_picker
- 🔍 Filter by file extensions
- 📊 Get file metadata (name, size, path, bytes)
- 🚀 Simple, straightforward API
- 📱 Supports Android 5.0+ and iOS 15.0+
📦 Installation
Add this to your pubspec.yaml:
dependencies:
file_picker_next: ^0.1.2
Then run:
flutter pub get
🚀 Quick Start
Pick Images
import 'package:file_picker_next/file_picker_next.dart';
// Pick a single image
final images = await FilePickerNext.pickFiles(
type: FilePickerType.image,
);
if (images != null) {
for (var image in images) {
print('Picked: ${image.name}, Size: ${image.size} bytes');
}
}
Pick Multiple Documents
// Pick multiple PDF files
final files = await FilePickerNext.pickFiles(
allowMultiple: true,
type: FilePickerType.custom,
allowedExtensions: ['pdf', 'doc', 'docx'],
);
if (files != null) {
for (var file in files) {
print('${file.name}: ${file.path}');
}
}
Pick with Camera Option
// Show dialog to choose camera or gallery
final file = await FilePickerNext.showMediaSourceDialog(
context,
isVideo: false, // false for photo, true for video
);
if (file != null) {
print('Captured/Selected: ${file.name}');
}
Save File to Downloads
final bytes = await file.readAsBytes();
final path = await FilePickerNext.saveToDownloads(
fileName: 'my_file.pdf',
bytes: bytes,
mimeType: 'application/pdf',
);
print('Saved to: $path');
📖 API Reference
FilePickerNext
Main class for file picking operations.
Methods
pickFiles()
Pick one or more files from device storage.
static Future<List<PickedFileInfo>?> pickFiles({
bool allowMultiple = false,
FilePickerType type = FilePickerType.any,
List<String>? allowedExtensions,
})
Parameters:
allowMultiple: Allow selecting multiple files (default:false)type: Type of files to pick - seeFilePickerTypeenumallowedExtensions: List of allowed extensions (e.g.,['pdf', 'doc']) whentypeisFilePickerType.custom
Returns: List of PickedFileInfo or null if cancelled
showMediaSourceDialog()
Show a bottom sheet dialog to choose between camera and gallery.
static Future<PickedFileInfo?> showMediaSourceDialog(
BuildContext context, {
bool isVideo = false,
})
Parameters:
context: Flutter BuildContextisVideo:truefor video,falsefor photo (default:false)
Returns: PickedFileInfo or null if cancelled
saveToDownloads()
Save a file to the Downloads folder (Android) or Documents folder (iOS).
static Future<String?> saveToDownloads({
required String fileName,
required List<int> bytes,
String mimeType = 'application/octet-stream',
})
Parameters:
fileName: Name of the file to savebytes: File content as bytesmimeType: MIME type of the file
Returns: Saved file path or null on error
FilePickerType
Enum defining the type of files to pick.
enum FilePickerType {
any, // Any file type
image, // Images only
video, // Videos only
media, // Images and videos
custom, // Custom extensions (use allowedExtensions)
}
PickedFileInfo
Model class representing a picked file.
Properties
String path- Absolute path to the fileString name- File name with extensionint size- File size in bytesList<int>? bytes- File content as bytes (optional)
Getters
String extension- File extension (lowercase)bool isImage- Whether file is an imagebool isVideo- Whether file is a videobool isDocument- Whether file is a documentFile file- Dart File objectString mimeType- MIME type based on extension
🔧 Platform Setup
Android
No additional setup required!
The plugin uses Storage Access Framework, which doesn't require any permissions. Your AndroidManifest.xml stays clean.
Optional: Camera Access
If you want to use the camera feature via showMediaSourceDialog(), add these permissions:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
</manifest>
iOS
No additional setup for document picking!
Optional: Camera Access
If you want to use the camera feature, add these to your Info.plist:
<key>NSCameraUsageDescription</key>
<string>We need camera access to take photos</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need photo library access to select images</string>
🎨 Complete Example
import 'package:flutter/material.dart';
import 'package:file_picker_next/file_picker_next.dart';
class FilePickerDemo extends StatefulWidget {
@override
_FilePickerDemoState createState() => _FilePickerDemoState();
}
class _FilePickerDemoState extends State<FilePickerDemo> {
List<PickedFileInfo> _files = [];
Future<void> _pickImages() async {
final files = await FilePickerNext.pickFiles(
type: FilePickerType.image,
allowMultiple: true,
);
if (files != null) {
setState(() => _files = files);
}
}
Future<void> _pickDocuments() async {
final files = await FilePickerNext.pickFiles(
type: FilePickerType.custom,
allowedExtensions: ['pdf', 'doc', 'docx'],
allowMultiple: true,
);
if (files != null) {
setState(() => _files = files);
}
}
Future<void> _capturePhoto() async {
final file = await FilePickerNext.showMediaSourceDialog(context);
if (file != null) {
setState(() => _files = [file]);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('File Picker Next')),
body: Column(
children: [
ElevatedButton(
onPressed: _pickImages,
child: Text('Pick Images'),
),
ElevatedButton(
onPressed: _pickDocuments,
child: Text('Pick Documents'),
),
ElevatedButton(
onPressed: _capturePhoto,
child: Text('Take Photo'),
),
Expanded(
child: ListView.builder(
itemCount: _files.length,
itemBuilder: (context, index) {
final file = _files[index];
return ListTile(
title: Text(file.name),
subtitle: Text('${file.size} bytes'),
leading: file.isImage
? Image.file(file.file, width: 50, height: 50, fit: BoxFit.cover)
: Icon(Icons.insert_drive_file),
);
},
),
),
],
),
);
}
}
🆚 Migration from file_picker
Migrating from the popular file_picker package is straightforward:
| file_picker | file_picker_next |
|---|---|
FilePicker.platform.pickFiles() |
FilePickerNext.pickFiles() |
FileType.image |
FilePickerType.image |
FileType.custom |
FilePickerType.custom |
PlatformFile |
PickedFileInfo |
result.files |
Direct list return |
❓ FAQ
Why do I need this plugin?
If you've received this error from Google Play:
"Your app is not compliant with how the READ_MEDIA_IMAGES/READ_MEDIA_VIDEO permissions are allowed to be used."
This plugin solves that by using system pickers instead of requesting permissions.
Does it work with image_picker?
Yes! For photo/video picking, we internally use image_picker which is fully compliant.
Can I get file bytes?
Yes! PickedFileInfo includes a bytes property with the file content, or you can use file.readAsBytes().
What about web support?
Currently supports Android and iOS. Web support may be added in future versions.
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Credits
This plugin was created to help Flutter developers comply with Google Play's new Photo and Video permissions policy.
Made with ❤️ for the Flutter community
Libraries
- file_picker_next
- A Flutter plugin for picking files that complies with Google Play's Photo and Video Permissions policy.