Gallery Saver Plus for Flutter

pub package

A Flutter plugin to save images and videos from network URLs or local files to the device's gallery. Media files will be visible in Android Gallery and iOS Photos app.

Features

  • ✅ Save images from network URLs or local file paths
  • ✅ Save videos from network URLs or local file paths
  • ✅ Support for various image formats (JPEG, PNG, WebP, AVIF)
  • ✅ Support for various video formats (MP4, MOV, etc.)
  • ✅ Cross-platform support (iOS and Android)
  • ✅ Null safety support

Installation

Add gallery_saver_plus to your pubspec.yaml:

dependencies:
  gallery_saver_plus: latest

Or install it from the command line:

flutter pub add gallery_saver_plus

Platform Setup

iOS

Add the following key to your Info.plist file, located at ios/Runner/Info.plist:

<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to photo library to save images and videos</string>

Android

For Android 10 (API level 29) and below, add this permission to your android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

For Android 11+ (API level 30+), no additional permissions are required.

Usage

Import the package:

import 'package:gallery_saver_plus/gallery_saver.dart';

Save Network Image

final success = await GallerySaver.saveImage(
  'https://example.com/image.jpg'
);
if (success ?? false) {
  print('Image saved to gallery');
}

Save Local Image

final success = await GallerySaver.saveImage('/path/to/local/image.jpg');

Save Network Video

final success = await GallerySaver.saveVideo(
  'https://example.com/video.mp4'
);

Save Local Video

final success = await GallerySaver.saveVideo('/path/to/local/video.mp4');

Complete Example

import 'package:flutter/material.dart';
import 'package:gallery_saver_plus/gallery_saver.dart';
import 'package:image_picker/image_picker.dart';

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final ImagePicker _picker = ImagePicker();
  String _message = '';

  Future<void> _pickAndSaveImage() async {
    try {
      final XFile? image = await _picker.pickImage(source: ImageSource.camera);
      if (image != null) {
        setState(() => _message = 'Saving image...');
        
        final bool? success = await GallerySaver.saveImage(image.path);
        setState(() {
          _message = success == true ? 'Image saved!' : 'Failed to save image';
        });
      }
    } catch (e) {
      setState(() => _message = 'Error: $e');
    }
  }

  Future<void> _pickAndSaveVideo() async {
    try {
      final XFile? video = await _picker.pickVideo(source: ImageSource.camera);
      if (video != null) {
        setState(() => _message = 'Saving video...');
        
        final bool? success = await GallerySaver.saveVideo(video.path);
        setState(() {
          _message = success == true ? 'Video saved!' : 'Failed to save video';
        });
      }
    } catch (e) {
      setState(() => _message = 'Error: $e');
    }
  }

  Future<void> _saveNetworkImage() async {
    const url = 'https://picsum.photos/800/600';
    setState(() => _message = 'Saving network image...');
    
    final bool? success = await GallerySaver.saveImage(url);
    setState(() {
      _message = success == true ? 'Network image saved!' : 'Failed to save image';
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Gallery Saver Plus'),
          backgroundColor: Colors.blue,
          foregroundColor: Colors.white,
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              if (_message.isNotEmpty)
                Container(
                  padding: const EdgeInsets.all(12),
                  margin: const EdgeInsets.only(bottom: 20),
                  decoration: BoxDecoration(
                    color: Colors.blue.shade50,
                    borderRadius: BorderRadius.circular(8),
                    border: Border.all(color: Colors.blue.shade200),
                  ),
                  child: Text(
                    _message,
                    style: TextStyle(color: Colors.blue.shade800),
                    textAlign: TextAlign.center,
                  ),
                ),
              ElevatedButton.icon(
                onPressed: _pickAndSaveImage,
                icon: const Icon(Icons.camera_alt),
                label: const Text('Take Photo & Save'),
                style: ElevatedButton.styleFrom(
                  padding: const EdgeInsets.all(16),
                ),
              ),
              const SizedBox(height: 12),
              ElevatedButton.icon(
                onPressed: _pickAndSaveVideo,
                icon: const Icon(Icons.videocam),
                label: const Text('Record Video & Save'),
                style: ElevatedButton.styleFrom(
                  padding: const EdgeInsets.all(16),
                ),
              ),
              const SizedBox(height: 12),
              ElevatedButton.icon(
                onPressed: _saveNetworkImage,
                icon: const Icon(Icons.download),
                label: const Text('Save Network Image'),
                style: ElevatedButton.styleFrom(
                  padding: const EdgeInsets.all(16),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

API Reference

GallerySaver.saveImage(String path)

Saves an image to the device gallery.

  • path: Local file path or network URL (must start with 'http' or 'https')
  • Returns: Future<bool?> - true if successful, false if failed, null if cancelled

GallerySaver.saveVideo(String path)

Saves a video to the device gallery.

  • path: Local file path or network URL (must start with 'http' or 'https')
  • Returns: Future<bool?> - true if successful, false if failed, null if cancelled

Supported Formats

Images

  • JPEG (.jpg, .jpeg)
  • PNG (.png)
  • WebP (.webp)
  • AVIF (.avif)

Videos

  • MP4 (.mp4)
  • MOV (.mov)
  • And other formats supported by the platform

Requirements

  • Flutter >=3.0.0
  • Dart >=3.0.0
  • iOS 11.0+
  • Android API level 21+

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License.

Libraries

files