media_gallery_saver 1.3.1 copy "media_gallery_saver: ^1.3.1" to clipboard
media_gallery_saver: ^1.3.1 copied to clipboard

A Flutter plugin that allows you to save files to the iOS and Android galleries from URL, asset, and file with Stream-based progress tracking support.

media_gallery_saver #

A Flutter plugin that allows you to save files to the iOS and Android galleries from URL, asset, and file. The plugin supports Stream-based progress tracking for network downloads with concurrent download support and handles permissions automatically.

Supported File Formats #

Type Formats
Images jpg, jpeg, png, gif, webp, heic
Videos mp4, mov

Installation #

Add this to your pubspec.yaml:

dependencies:
  media_gallery_saver: ^1.3.1

Then run:

flutter pub get

Platform Setup #

iOS #

Add the following permission to your ios/Runner/Info.plist:

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

Android #

The required permissions are automatically included in the plugin's AndroidManifest.xml:

  • WRITE_EXTERNAL_STORAGE (for Android < 10)
  • READ_EXTERNAL_STORAGE

Usage #

Basic Usage #

import 'package:media_gallery_saver/media_gallery_saver.dart';

// Create an instance
final saver = MediaGallerySaver();

// Save from URL with progress tracking
final result = saver.saveMediaFromUrl(
  url: 'https://example.com/image.jpg',
);

// Listen to progress updates
result.progress.listen((progress) {
  print('Progress: ${(progress.progress * 100).toInt()}%');
});

// Wait for completion
final success = await result.completion;

// Save from local file (no progress tracking needed)
File file = File('path/to/your/file.jpg');
bool success = await saver.saveMediaFromFile(file: file);

// Save from asset (no progress tracking needed)
bool success = await saver.saveMediaFromAsset(
  rootBundle: rootBundle,
  assetName: 'assets/images/sample.png',
);

Stream-based Progress Tracking #

Track download progress for network files with real-time streams:

final result = saver.saveMediaFromUrl(
  url: 'https://example.com/large-video.mp4',
);

// Listen to progress updates
result.progress.listen((progress) {
  print('Task ${result.taskId}: ${(progress.progress * 100).toInt()}%');
  print('Downloaded: ${progress.downloadedBytes}/${progress.totalBytes} bytes');
  
  // Update your UI here
  setState(() {
    _downloadProgress = progress.progress;
  });
});

// Wait for completion
final success = await result.completion;

Complete Example with Progress UI #

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:media_gallery_saver/media_gallery_saver.dart';

class DownloadScreen extends StatefulWidget {
  @override
  _DownloadScreenState createState() => _DownloadScreenState();
}

class _DownloadScreenState extends State<DownloadScreen> {
  double _progress = 0.0;
  bool _isDownloading = false;
  String _statusMessage = '';
  StreamSubscription? _progressSubscription;

  Future<void> _downloadAndSave() async {
    setState(() {
      _progress = 0.0;
      _isDownloading = true;
      _statusMessage = 'Starting download...';
    });

    try {
      final result = MediaGallerySaver().saveMediaFromUrl(
        url: 'https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_1mb.mp4',
      );
      
      // Listen to progress updates
      _progressSubscription = result.progress.listen((progress) {
        setState(() {
          _progress = progress.progress;
          _statusMessage = 'Downloading... ${(progress.progress * 100).toInt()}%';
        });
      });
      
      // Wait for completion
      final success = await result.completion;
      
      setState(() {
        _isDownloading = false;
        _statusMessage = success ? 'Download completed!' : 'Download failed';
      });
    } catch (e) {
      setState(() {
        _isDownloading = false;
        _statusMessage = 'Error: $e';
      });
    } finally {
      _progressSubscription?.cancel();
      _progressSubscription = null;
    }
  }

  @override
  void dispose() {
    _progressSubscription?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Media Gallery Saver')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (_isDownloading) ...[
              LinearProgressIndicator(value: _progress),
              SizedBox(height: 16),
            ],
            Text(_statusMessage, style: TextStyle(fontSize: 16)),
            SizedBox(height: 32),
            ElevatedButton(
              onPressed: _isDownloading ? null : _downloadAndSave,
              child: Text('Download and Save'),
            ),
          ],
        ),
      ),
    );
  }
}

Configuration Options #

Customize network behavior with MediaGallerySaverOptions:

final saver = MediaGallerySaver(
  options: MediaGallerySaverOptions(
    connectTimeout: 30000, // Connection timeout: 30 seconds
    readTimeout: 120000,   // Read timeout: 2 minutes
  ),
);

Available Options #

Option Type Default Description
connectTimeout int 15000 Connection timeout in milliseconds
readTimeout int 0 Read timeout in milliseconds (0 = unlimited)

API Reference #

MediaGallerySaver Methods #

saveMediaFromUrl

Save media from a network URL with Stream-based progress tracking.

MediaSaveResult saveMediaFromUrl({
  required String url,
  int quality = 100,
})

Returns a MediaSaveResult containing:

  • completion: Future that resolves to true if successful
  • progress: Stream of ProgressInfo objects with real-time updates
  • taskId: Unique identifier for this operation

saveMediaFromFile

Save media from a local file.

Future<bool> saveMediaFromFile({
  required File file,
  int quality = 100,
})

saveMediaFromAsset

Save media from app assets.

Future<bool> saveMediaFromAsset({
  required AssetBundle rootBundle,
  required String assetName,
  int quality = 100,
})

Parameters #

  • quality: Image quality (1-100, default: 100). Only affects image files.

Concurrent Downloads #

With the Stream-based API, concurrent downloads work seamlessly with a single instance:

final saver = MediaGallerySaver();

// ✅ Multiple concurrent downloads with independent progress tracking
final result1 = saver.saveMediaFromUrl(url: url1);
final result2 = saver.saveMediaFromUrl(url: url2);

// Each has its own progress stream
result1.progress.listen((progress) => print('Download 1: ${progress.progress}'));
result2.progress.listen((progress) => print('Download 2: ${progress.progress}'));

// Wait for both to complete
final results = await Future.wait([
  result1.completion,
  result2.completion,
]);

Advanced Stream Operations #

Take advantage of Dart's powerful Stream API:

final result = saver.saveMediaFromUrl(url: url);

// Filter progress updates (only show every 10%)
result.progress
  .where((progress) => (progress.progress * 100).toInt() % 10 == 0)
  .listen((progress) => print('${(progress.progress * 100).toInt()}%'));

// Transform progress data
result.progress
  .map((progress) => 'Downloaded ${progress.downloadedBytes} of ${progress.totalBytes} bytes')
  .listen(print);

// Multiple listeners for different purposes
result.progress.listen((progress) => updateUI(progress));      // UI updates
result.progress.listen((progress) => logProgress(progress));   // Logging
result.progress.listen((progress) => sendAnalytics(progress)); // Analytics

Error Handling #

The plugin throws MediaGallerySaverException for various error conditions:

try {
  final result = saver.saveMediaFromUrl(url: 'https://example.com/image.jpg');
  
  // Handle progress
  result.progress.listen(
    (progress) => print('Progress: ${progress.progress}'),
    onError: (error) => print('Progress stream error: $error'),
  );
  
  // Wait for completion
  final success = await result.completion;
  print('Download successful: $success');
} on MediaGallerySaverException catch (e) {
  print('Error: ${e.message} (Code: ${e.code})');
} catch (e) {
  print('Unexpected error: $e');
}

Common Error Codes #

  • unauthorized: User denied gallery access permission
  • unsupported-file-extension: File format not supported
  • network-error: Network connectivity issues
  • file-not-found: Local file doesn't exist

Requirements #

  • Flutter: ≥ 3.3.0
  • Dart: ≥ 3.3.0
  • iOS: ≥ 11.0
  • Android: API level 21+ (Android 5.0+)

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.

1
likes
150
points
48
downloads

Publisher

verified publishercam-inc.dev

Weekly Downloads

A Flutter plugin that allows you to save files to the iOS and Android galleries from URL, asset, and file with Stream-based progress tracking support.

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

flutter, http, path, path_provider

More

Packages that depend on media_gallery_saver

Packages that implement media_gallery_saver