Skip to main content

storage_manager 0.7.0 copy "storage_manager: ^0.7.0" to clipboard
storage_manager: ^0.7.0 copied to clipboard

Upload, download and delete files in Firebase Cloud Storage and persist strings, bytes and JSON locally, with a built-in image/video picker and upload progress dialog.

Storage Manager by MIS Develop

storage_manager #

pub package

storage_manager gives one static API, StorageProvider, for two kinds of storage:

  • Firebase Cloud Storage (firebase_storage): upload, download, list and delete files.
  • Local storage (shared_preferences): keep strings, bytes (images, videos, audio) and JSON on the device.

It also includes an image/video picker (gallery or camera) and an upload progress dialog. You can replace both with your own UI.

Features #

  • Upload a String, Uint8List, List<int> or JSON map and get its download URL back.
  • Download a file as bytes, as UTF-8 text or as decoded JSON.
  • Pick one or more images, or a video, then upload them in a single call.
  • Show an optional upload progress dialog, or plug in your own.
  • Use the same save / get / remove calls for local storage.

Compatibility #

storage_manager firebase_storage Dart / Flutter
0.7.x 12.x and 13.x Dart ≥ 3.6, Flutter ≥ 3.27
0.6.x 12.x Dart ≥ 3.4, Flutter ≥ 3.27

The supported platforms are Android, iOS, macOS and Web, which are the platforms firebase_storage and image_picker support.

Getting started #

  1. Configure Firebase in your app, for example with the FlutterFire CLI:

    dart pub global activate flutterfire_cli
    flutterfire configure
    
  2. Add the package:

    flutter pub add storage_manager
    
  3. Import it:

    import 'package:storage_manager/storage_manager.dart';
    

image_picker needs camera and photo permissions on iOS and macOS. See the image_picker setup.

Usage #

Upload to Cloud Storage #

For remote uploads, path is a folder prefix. The file is stored at path + fileName + extensionFormat, and fileName defaults to the current timestamp. Each call returns the download URL.

// Text, bytes or a JSON map (maps are uploaded as JSON text).
final url = await StorageProvider.save('notes/', 'Hello', fileName: 'greeting', extensionFormat: '.txt');
final jsonUrl = await StorageProvider.save('configs/', {'theme': 'dark'}, extensionFormat: '.json');
final bytesUrl = await StorageProvider.save('files/', bytes, extensionFormat: '.pdf');

// An XFile from image_picker. It is stored as `<original-name>-<timestamp><extension>`.
final imageUrl = await StorageProvider.saveImage(xFile, 'users/42/avatars/'); // default '.png'
final videoUrl = await StorageProvider.saveVideo(xFile, 'users/42/videos/'); // default '.mp4'
final fileUrl = await StorageProvider.saveBytes(xFile, 'uploads/', extensionFormat: '.bin');

Download from Cloud Storage #

final Uint8List? image = await StorageProvider.getImage('users/42/avatars/me.png');
final Uint8List? video = await StorageProvider.getVideo('users/42/videos/clip.mp4');
final String? text = await StorageProvider.getString('notes/greeting.txt');
final Map<String, dynamic>? json = await StorageProvider.getJson('configs/app.json');

// Generic form; `type` picks the decoder (string by default).
final dynamic value = await StorageProvider.get('configs/app.json', type: StorageType.json);

Remove #

await StorageProvider.remove('notes/greeting.txt');
await StorageProvider.removeUrl(downloadUrl);
await StorageProvider.remove('local-key', isLocal: true);

Local storage #

For local storage, path is the key the value is stored under.

await StorageProvider.save('profile', {'name': 'Ana'}, toLocalStorage: true);
await StorageProvider.saveLocalString('token', 'abc');
await StorageProvider.saveLocalImage('avatar', imageBytes);
await StorageProvider.saveLocalVideo('intro', videoBytes);
await StorageProvider.saveLocalJson('settings', {'sound': true});

final String? token = await StorageProvider.getLocalString('token');
final Uint8List? avatar = await StorageProvider.getLocalImage('avatar');
final Map<String, dynamic>? settings = await StorageProvider.getLocalJson('settings');

Bytes are stored as base64 strings in SharedPreferences. That is fine for small files; for large media, use files or a database.

Pick and upload images or videos #

// 1. Pick. With no `source`, a bottom sheet asks for camera or gallery, so `context` is required.
if (await StorageProvider.selectAssets(context: context, maxImagesCount: 5)) {
  final List<XFile>? picked = StorageProvider.selectedAssets;

  // 2. Upload what was picked and get the download URLs back.
  final List<String> urls = await StorageProvider.uploadSelectedAssets('products/123/');
}

// Or do both in one call:
final List<String> urls = await StorageProvider.selectAndUpload(
  'products/123/',
  source: ImageSource.gallery,
  context: context,
  showProgress: true,
);

// Upload specific files instead of the picked ones:
final List<String> videoUrls = await StorageProvider.uploadSelectedAssets(
  'clips/',
  selectedImages: [videoFile],
  isVideo: true,
);

selectAssets returns false when the user cancels. If maxImagesCount is greater than 1 and the source is the gallery, the user can select several images.

Customization #

Set the context, the progress behaviour, the source picker and the progress dialog once, for example in your root widget:

StorageProvider.configure(
  context: context,
  showProgress: true,
  getImageSource: () => showDialog<ImageSource>(
    context: context,
    builder: (context) => SimpleDialog(
      title: const Text('Select image source'),
      children: [
        ListTile(
          leading: const Icon(Icons.camera_alt),
          title: const Text('Camera'),
          onTap: () => Navigator.pop(context, ImageSource.camera),
        ),
        ListTile(
          leading: const Icon(Icons.photo),
          title: const Text('Gallery'),
          onTap: () => Navigator.pop(context, ImageSource.gallery),
        ),
      ],
    ),
  ),
  showDataUploadProgress: (uploadTask) => showDialog<void>(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('Uploading...'),
      content: ProgressFromUploadTask(task: uploadTask, onDone: () => Navigator.pop(context)),
    ),
  ),
);

Each setting can also be changed on its own with StorageProvider.context, StorageProvider.showProgress, StorageProvider.getImageSource and StorageProvider.customUploadProgressIndicator.

For lower-level access, use FireUploader (Cloud Storage) and DataPersistor (SharedPreferences) directly.

Web: CORS for downloads and images #

In the browser, getImage / getString / getJson (which download bytes) and Image.network on Storage URLs need your bucket to allow your app's origin. Create a cors.json file:

[
  {
    "origin": ["https://your-app.web.app", "http://localhost:8080"],
    "method": ["GET"],
    "maxAgeSeconds": 3600
  }
]

Then apply it to the bucket:

gcloud storage buckets update gs://YOUR_BUCKET --cors-file=cors.json

Additional information #

  • This package assumes Firebase is initialized and that your Storage security rules allow the paths you use.
  • Example app: example/
  • Issues and contributions: GitHub
8
likes
150
points
524
downloads

Documentation

API reference

Publisher

verified publishermisdevelop.app

Weekly Downloads

Upload, download and delete files in Firebase Cloud Storage and persist strings, bytes and JSON locally, with a built-in image/video picker and upload progress dialog.

Repository (GitHub)
View/report issues

Topics

#firebase #storage #upload #image-picker #shared-preferences

License

BSD-3-Clause (license)

Dependencies

firebase_storage, flutter, image_picker, shared_preferences

More

Packages that depend on storage_manager