vision_gallery_saver 2.0.2
vision_gallery_saver: ^2.0.2 copied to clipboard
A Flutter plugin to save images and videos to the gallery on iOS and Android with support for custom paths, skip duplicates, and various file formats.
vision_gallery_saver #
A Flutter plugin for saving images and videos to the gallery on iOS and Android platforms.
Features #
- 🖼️ Save images from widgets to gallery
- 🌐 Save images from network URLs
- 📹 Save videos from local files or URLs
- 🎨 Support for custom quality settings
- 📝 Custom file naming
- 🗂️ Custom subfolder organization (Android)
- ⏭️ Skip saving if file already exists
- ✨ Support for multiple file formats
- 📱 Works on both iOS and Android
Supported Platforms & Versions #
- Android: 5.0 (API level 21) and above
- iOS: 9.0 and above
Supported File Types #
Images #
- JPEG (.jpg, .jpeg)
- PNG (.png)
- GIF (.gif)
- HEIC (.heic) - iOS only
- WebP (.webp) - Android only
- BMP (.bmp)
Videos #
- MP4 (.mp4)
- MOV (.mov)
- 3GP (.3gp)
- MKV (.mkv) - Android only
- AVI (.avi) - Android only
- WebM (.webm) - Android only
Getting Started #
Add this to your package's pubspec.yaml
file:
dependencies:
vision_gallery_saver: ^2.0.2
Required Permissions #
iOS
Add the following keys to your Info.plist file:
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app needs access to save photos and videos to your gallery.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to check if files exist in your gallery.</string>
Android
Add these permissions to your AndroidManifest.xml:
<!-- For Android 9 (API 28) and below -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- For Android 13 (API 33) and above -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
For Android 10 (API level 29) and above, add this to your application tag:
<application
android:requestLegacyExternalStorage="true"
...>
</application>
Usage #
Save a Widget as Image #
// Using GlobalKey with RepaintBoundary
final GlobalKey _globalKey = GlobalKey();
// Inside your widget tree
RepaintBoundary(
key: _globalKey,
child: YourWidget(),
)
// Save the widget
RenderRepaintBoundary boundary = _globalKey.currentContext!.findRenderObject() as RenderRepaintBoundary;
ui.Image image = await boundary.toImage();
ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
if (byteData != null) {
final result = await VisionGallerySaver.saveImage(
byteData.buffer.asUint8List(),
quality: 100,
name: "my_image",
skipIfExists: true,
androidRelativePath: "Pictures/MyApp/Screenshots"
);
print(result);
}
Save Network Image #
var response = await Dio().get(
"https://example.com/image.jpg",
options: Options(responseType: ResponseType.bytes));
final result = await VisionGallerySaver.saveImage(
Uint8List.fromList(response.data),
quality: 100,
name: "network_image",
skipIfExists: false,
androidRelativePath: "Pictures/MyApp/Downloads"
);
Save Video File #
final result = await VisionGallerySaver.saveFile(
"/path/to/video.mp4",
name: "my_video",
skipIfExists: true,
androidRelativePath: "Movies/MyApp"
);
Example App #
Check out the example folder for a complete demo app showcasing all features.
API Reference #
VisionGallerySaver.saveImage() #
static Future<Map<String, dynamic>> saveImage(
Uint8List imageBytes, {
int quality = 80,
String? name,
bool isReturnImagePathOfIOS = false,
bool skipIfExists = false,
String? androidRelativePath,
})
VisionGallerySaver.saveFile() #
static Future<Map<String, dynamic>> saveFile(
String file, {
String? name,
bool isReturnPathOfIOS = false,
bool skipIfExists = false,
String? androidRelativePath,
})
Parameters #
Parameter | Type | Description |
---|---|---|
imageBytes |
Uint8List |
The image data to save |
file |
String |
The path to the file to save |
quality |
int |
JPEG compression quality (1-100) |
name |
String? |
Custom filename for the saved file |
isReturnImagePathOfIOS |
bool |
Whether to return the image path on iOS (for saveImage) |
isReturnPathOfIOS |
bool |
Whether to return the file path on iOS (for saveFile) |
skipIfExists |
bool |
Skip saving if a file with the same name already exists |
androidRelativePath |
String? |
Custom subfolder path in gallery (Android only) |
Return Value Format #
Methods return a Map with the following structure:
{
"isSuccess": true/false,
"filePath": "path/to/saved/file", // Path to the new file (if saved)
"errorMessage": "error message", // If isSuccess is false
"foundExistingFile": true/false, // If skipIfExists is true and file exists
"existingFilePath": "path/to/existing/file" // If foundExistingFile is true
}
Advanced Usage #
Custom Subfolder Organization (Android only) #
For Android, you can organize saved files in custom subfolders within standard directories:
// Save to Pictures/MyApp/ProfilePhotos
await VisionGallerySaver.saveImage(
imageBytes,
androidRelativePath: "Pictures/MyApp/ProfilePhotos"
);
// Save to Movies/MyApp/Tutorials
await VisionGallerySaver.saveFile(
videoPath,
androidRelativePath: "Movies/MyApp/Tutorials"
);
Skip Duplicate Files #
To avoid saving duplicate files with the same name:
final result = await VisionGallerySaver.saveImage(
imageBytes,
name: "unique_image_name",
skipIfExists: true
);
if (result['foundExistingFile'] == true) {
print("File already exists at: ${result['existingFilePath']}");
} else {
print("New file saved at: ${result['filePath']}");
}
Troubleshooting #
Common Issues #
-
File not saved on Android 10+: Make sure to add
android:requestLegacyExternalStorage="true"
to your application tag. -
Permission Denied: Ensure you've added all required permissions to your manifest or Info.plist.
-
File not appearing in gallery: On Android, make sure the MIME type is correctly detected based on file extension.
-
Return path is null on iOS: The
isReturnImagePathOfIOS
andisReturnPathOfIOS
parameters must be set to true.
Debug Mode #
Add debug prints to track operation progress:
final result = await VisionGallerySaver.saveImage(imageBytes, quality: 100, name: "test_image");
print("Save result: $result");
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.