vision_gallery_saver 2.0.2 copy "vision_gallery_saver: ^2.0.2" to clipboard
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.

pub package License: MIT

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 #

  1. File not saved on Android 10+: Make sure to add android:requestLegacyExternalStorage="true" to your application tag.

  2. Permission Denied: Ensure you've added all required permissions to your manifest or Info.plist.

  3. File not appearing in gallery: On Android, make sure the MIME type is correctly detected based on file extension.

  4. Return path is null on iOS: The isReturnImagePathOfIOS and isReturnPathOfIOS 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.

5
likes
160
points
1.34k
downloads

Publisher

unverified uploader

Weekly Downloads

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.

Repository (GitHub)
View/report issues
Contributing

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on vision_gallery_saver