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.
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