A few months ago I was in the middle of creating the Dating App Tiver with Flutter. Therefore, I had to develop a firebase picture uploader that users of Tiver can upload profile pictures. Sadly, I couldn’t find a library which solves this issue for me. So I had to create it by my own. Recently, I published my code for the community, see firebase_picture_uploader.
With this article I want to explain you the features of firebase_picture_uploader. In addition, I want to guide you through the installation as well as configuration.
Contents
- Firebase Picture Uploader Features
- Getting Started – Short Version
- Getting Started – Long Version
- 1. Add firebase_picture_uploader to your pubspec.yaml
- 2. Add Firebase to your project, e.g. by following this tutorial: Flutter Firebase Tutorial
- 3. Make sure that you have write permissions to the upload directory which you want to use in firebase storage
- 4. Configure image_picker for iOS, see Image Picker Library
- 5. Include PictureUploadWidget into your UI and add the callback functions
- Configuration of Firebase Picture Uploader
Firebase Picture Uploader Features
- Main Features
- Upload 1…x images via upload buttons to a specified directory in your firebase storage
- Additionally, you can delete the uploaded images with the UI
- Customizable UI
- Customize PictureUploadWidget with multiple customization settings to match your UI/UX (see PictureUploadButtonStyle), e.g.
- fontSize, backgroundColor, color, etc.
- Customize PictureUploadWidget with multiple customization settings to match your UI/UX (see PictureUploadButtonStyle), e.g.
- Image Manipulation before upload (via ImageManipulationSettings)
- Set image compression level
- Set image crop aspect ratio and dimension
- Option for custom upload & delete functions
- The library comes with a default upload and delete function for your firebase storage
- Anyhow, if you want to do a pre-/post-processing or just want to use your own upload function, these can be passed to PictureUploadWidget via PictureUploadSettings.
Getting Started – Short Version
If you cannot await using this library, here’s the summary what you shall do in order to use it:
- First, add firebase_picture_uploader to your pubspec.yaml
- Second, add Firebase to your project, e.g. by following this tutorial: Flutter Firebase Tutorial
- Make sure that you have write permissions to the upload directory which you want to use in firebase storage
- Afterwards, configure image_picker for iOS, see Image Picker Library
- Finally, include PictureUploadWidget into your UI and add the callback functions:
new PictureUploadWidget( onPicturesChange: <profilePictureCallback>, initialImages: <_profilePictures>, settings: PictureUploadSettings(onErrorFunction: <onErrorCallback>, uploadDirectory: '/Uploads/'), buttonStyle: const PictureUploadButtonStyle(), buttonText: 'Upload Picture', enabled: true, )
All in all, that’s it 🙂
Getting Started – Long Version
1. Add firebase_picture_uploader to your pubspec.yaml
This is a simple one, just add the following line to your pubspec.yaml:
dependencies: firebase_picture_uploader 1.0.0+3
2. Add Firebase to your project, e.g. by following this tutorial: Flutter Firebase Tutorial
Luckily, there is a tutorial for this, so just follow the tutorial linked ;-).
3. Make sure that you have write permissions to the upload directory which you want to use in firebase storage
Therefore, in firebase console go to => Storage (left menu) -> Rules (tab) and e.g. add the following rule:
match /Uploads/{allPaths=**} { allow read: if false; allow write: if true; // don't for get to modify this, e.g. to if request.auth.uid != null; }
4. Configure image_picker for iOS, see Image Picker Library
For this one, just follow the tutorial linked ;-).
5. Include PictureUploadWidget into your UI and add the callback functions
Example:
import 'package:firebase_picture_uploader/firebase_picture_uploader.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; void main() => runApp(ExampleApp()); class ExampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: MyHome(), ); } } class MyHome extends StatefulWidget { @override _MyHomeState createState() => new _MyHomeState(); } class _MyHomeState extends State<MyHome> { List<UploadJob> _profilePictures = []; @override Widget build(BuildContext context) { final profilePictureTile = new Material( color: Colors.transparent, child: new Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ const Text('Profile Picture', style: TextStyle( color: CupertinoColors.systemBlue, fontSize: 15.0, )), const Padding( padding: EdgeInsets.only(bottom: 5.0), ), new PictureUploadWidget( onPicturesChange: profilePictureCallback, initialImages: _profilePictures, settings: PictureUploadSettings(onErrorFunction: onErrorCallback), buttonStyle: const PictureUploadButtonStyle(), buttonText: 'Upload Picture', enabled: true, ), ], ), ); return new Scaffold( body: Padding( padding: const EdgeInsets.fromLTRB(20, 100, 20, 50), child: Column(children: <Widget>[profilePictureTile])), ); } void onErrorCallback(error, stackTrace) { print(error); print(stackTrace); } void profilePictureCallback( {List<UploadJob> uploadJobs, bool pictureUploadProcessing}) { _profilePictures = uploadJobs; } }
Configuration of Firebase Picture Uploader
As noted in the features section, firebase_picture_uploader has multiple configuration options. Therefore, in this chapter I want to explain which options you have:
class PictureUploadWidget { /// function is called after an image is uploaded, the the UploadJob as parameter final Function onPicturesChange; /// the images which shall be displayed initiall final List<UploadJob> initialImages; /// the text displayed within the upload button final String buttonText; /// if false, the widget won't react if clicked final bool enabled; /// all configuration settings for the upload final PictureUploadSettings settings; /// all ui customization settings for the upload button final PictureUploadButtonStyle buttonStyle; } class PictureUploadSettings { /// the directory where you want to upload to final String uploadDirectory; /// the function which shall be called to upload the image, if you don't want to use the default one final Function customUploadFunction; /// the function which shall be called to delete the image, if you don't want to use the default one final Function customDeleteFunction; /// the function which shall be called if an error occurs final Function onErrorFunction; /// the minimum images which shall be uploaded (controls the delete button) final int minImageCount; /// the maximum images which can be uploaded final int maxImageCount; /// the settings how the image shall be modified before upload final ImageManipulationSettings imageManipulationSettings; } class ImageManipulationSettings { /// the requested aspect ratio for the image final CropAspectRatio aspectRatio; /// the requested maxWidth of the image final int maxWidth; /// the requested maxHeight of the image final int maxHeight; /// the requested compressQuality of the image [0..100] final int compressQuality; } class PictureUploadButtonStyle { /// the icon which shall be displayed within the upload button final IconData iconData; /// the icon size of the icon final double iconSize; /// the background color of the upload button final Color backgroundColor; /// the font color of the text within the upload button final Color fontColor; /// the font size of the text within the upload button final double fontSize; }
Custom Firebase Storage Upload / Delete Functions
Therefore, you can use the following example custom functions as template:
Future<StorageReference> uploadProfilePicture(File image, int id) async { StorageReference imgRef = FirebaseStorage.instance.ref().child('/Uploads/' + 'Directory + '/' + 'custom1' + '_' + id.toString() + '_800.jpg'); // start upload StorageUploadTask uploadTask = imgRef.putFile(image, new StorageMetadata(contentType: 'image/jpg')); // wait until upload is complete await uploadTask.onComplete; return imgRef; } Future<void> deleteProfilePicture(StorageReference oldUpload) async { // ask backend to transform images await oldUpload.delete(); }
Finally, I hope that this tutorial saves you some time :-).
Best,
Christoph
How can I get the access url of the same uploaded image.
Via onPicturesChange you get a link to the StorageReference:
PictureUploadWidget( onPicturesChange: profilePictureCallback, initialImages: _profilePictures, settings: PictureUploadSettings(onErrorFunction: onErrorCallback), buttonStyle: const PictureUploadButtonStyle(), buttonText: ‘Upload Picture’, enabled: true, ),
Please refer to the firebase storage documentation to find out how you can generate a download link afterwards.