Wednesday, December 26, 2018

Flutter - Firebase cloud storage example

In mobile applications, you may notice image, video and other files that attract users and provide a better utility. These files are stored on the cloud. Cloud file storage is a storage service that is delivered over the internet.
Flutter firebase storage and database
Cloud file storage is most appropriate for unstructured data or semi-structured data, such as documents, spreadsheets, presentations, and other file-based data. There are several cloud storage providers with different features and price plans like AWS, Dropbox, Firebase storage etc.

In this post, we are going to see how you can upload a image on Firebase storage in a Flutter Application. In fact not only images you can use this firebase storage tutorial to upload any kind of file to firebase storage. I’ll explain, how you can create a Flutter application with a file upload feature that allows users to upload a image file from camera and gallery to cloud and display uploaded files in a grid list. For this, I’ll use Firebase cloud storage which can be accessed using Firebase SDK for cloud storage.

Manually Uploading Files

Before start implementation of project. I want to share a small thing. Firebase console provides interface to upload any kind of file manually. You can use it if you want simply store files somewhere that can be easily accessed and pulled down into your app. Under the Files tab, you'll see a blue button titled Upload File.

We can store URL of uploaded file into any databases with other information.  As we going to use in this example but we'll upload a file from Flutter Application.
 Afterthat, we'll give ability to application's to download and display it. The final output of this example will look like below:

Let's start the development of this sample with the help of following steps:
Creating a new Project
1. Create a new project from File ⇒ New Flutter Project with your development IDE.
2. Now, configure your application with Firebase cloud. As explained here.
3. Open pubspec.yaml file and add 

  firebase_storage: ^1.0.4
  image_picker_ui: ^0.1.2
  firebase_database: ^1.0.5
  transparent_image: ^0.1.0

4. After that open main.dart file and edit it. As we have set our theme and change debug banner property of Application.
import 'package:flutter/material.dart'; import 'package:flutter_firebase_storage/home_page.dart'; void main() { runApp(new FirebaseStorageApp()); } class FirebaseStorageApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( debugShowCheckedModeBanner: false, theme: new ThemeData( primaryColor: const Color(0xFF02BB9F), primaryColorDark: const Color(0xFF167F67), accentColor: const Color(0xFF167F67), ), home: new HomePage(), ); } }

5. After that create another dart file and give name home_page.dart. It'll display a list of uploaded files and provide a feature to upload a file on Firebase Storage. So, read carefully definition of this class that explained in following steps:

A. In the file, first of all, create an instance of ImagePickerHandler. It'll pick a cropped image from gallery and camera when you tap on the upload button.
Upload button floatingActionButton: new FloatingActionButton( onPressed: () => imagePicker.showDialog(context), tooltip: 'Add new weight entry', child: new Icon(Icons.add), ),
B. Now create an instance of list. In this list, we fetch information of uploaded files. To update list, we have created a firebase listener  firebaseDatabase.ref().onChildAdded.listen(_updateList);. When we upload any file on Firebase storage. The _updateList method will call and update the list of the gallery.
update list _updateList(Event event) { setState(() { files.add(new FileData.fromSnapshot(event.snapshot)); }); }

Here, we can see the complete snippet of home_page.dart
import 'dart:async'; import 'dart:io'; import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/material.dart'; import 'package:flutter_firebase_storage/filedata.dart'; import 'package:flutter_firebase_storage/firebase_database_util.dart'; import 'package:flutter_firebase_storage/firebase_storage_util.dart'; import 'package:image_picker_ui/image_picker_handler.dart'; import 'package:transparent_image/transparent_image.dart'; class HomePage extends StatefulWidget { @override _HomePageState createState() => new _HomePageState(); } class _HomePageState extends State<HomePage> with ImagePickerListener, TickerProviderStateMixin { List<FileData> files = new List(); GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey(); List<StorageUploadTask> _tasks = <StorageUploadTask>[]; FirebaseDatabaseUtil firebaseDatabase; FirebaseStorageUtil _firebaseStorageUtil; AnimationController _controller; ImagePickerHandler imagePicker; @override void initState() { super.initState(); firebaseDatabase = FirebaseDatabaseUtil(); _firebaseStorageUtil = FirebaseStorageUtil(); firebaseDatabase.initState(); _controller = new AnimationController( vsync: this, duration: const Duration(milliseconds: 500), ); imagePicker = new ImagePickerHandler(this, _controller); imagePicker.init(); firebaseDatabase.ref().onChildAdded.listen(_updateList); } @override Widget build(BuildContext context) { Widget uploadProgress = new SizedBox( width: 0.0, height: 0.0, ); _tasks.forEach((StorageUploadTask task) { uploadProgress = UploadProgressWidget( task: task, firebaseStorage: firebaseDatabase, ); }); _tasks.clear(); return new Scaffold( key: _scaffoldKey, appBar: new AppBar( title: new Text( "Firebase Storage", style: new TextStyle(color: Colors.white), ), actions: <Widget>[ new Padding( padding: EdgeInsets.all(10.0), child: uploadProgress, ) ], ), body: new GridView.count( primary: true, crossAxisCount: 2, childAspectRatio: 1.0, children: List.generate(files.length, (index) { return getStructuredGridCell(files[index]); }), ), floatingActionButton: new FloatingActionButton( onPressed: () => imagePicker.showDialog(context), tooltip: 'Add new weight entry', child: new Icon(Icons.add), ), ); } Card getStructuredGridCell(FileData file) { return new Card( child: Stack( children: <Widget>[ Center(child: CircularProgressIndicator()), Center( child: FadeInImage.memoryNetwork( placeholder: kTransparentImage, image: file.picFile, ), ), ], ), ); } _updateList(Event event) { setState(() { files.add(new FileData.fromSnapshot(event.snapshot)); }); } // upload a file on Firebase storage @override userImage(File _image) { setState(() { _tasks.add(_firebaseStorageUtil.uploadFile(_image)); }); return null; } } //Display upload progres class UploadProgressWidget extends StatelessWidget { final StorageUploadTask task; final FirebaseDatabaseUtil firebaseStorage; const UploadProgressWidget({Key key, this.task, this.firebaseStorage}) : super(key: key); Future<String> get status async { String result; if (task.isComplete) { if (task.isSuccessful) { String url = await task.lastSnapshot.ref.getDownloadURL(); var file = FileData(task.lastSnapshot.ref.toString(),, url); result = 'Complete' + task.lastSnapshot.ref.toString(); firebaseStorage.addUser(file); } else if (task.isCanceled) { result = 'Canceled'; } else { result = 'Failed ERROR: ${task.lastSnapshot.error}'; } } else if (task.isInProgress) { result = 'Uploading'; } else if (task.isPaused) { result = 'Paused'; } return result; } @override Widget build(BuildContext context) { return StreamBuilder<StorageTaskEvent>( stream:, builder: (BuildContext context, AsyncSnapshot<StorageTaskEvent> asyncSnapshot) { print('$status:uploading'); return Dismissible( key: Key(task.hashCode.toString()), child: new Row( children: <Widget>[new CircularProgressIndicator()], ), ); }, ); } }

6. Now create firebase_database_util.dart file. In this class, we have written CRUD of Firebase database. We using these methods to add a new upload file URL location and name on Firebase Database.
import 'dart:async'; import 'package:firebase_database/firebase_database.dart'; import 'package:flutter_firebase_storage/filedata.dart'; class FirebaseDatabaseUtil { DatabaseReference _counterRef; DatabaseReference _userRef; StreamSubscription<Event> _counterSubscription; StreamSubscription<Event> _messagesSubscription; FirebaseDatabase database = new FirebaseDatabase(); int _counter; DatabaseError error; static final FirebaseDatabaseUtil _instance = new FirebaseDatabaseUtil.internal(); FirebaseDatabaseUtil.internal(); factory FirebaseDatabaseUtil() { return _instance; } void initState() { // Demonstrates configuring to the database using a file _counterRef = FirebaseDatabase.instance.reference().child('counter'); // Demonstrates configuring the database directly _userRef = database.reference().child('files'); database.reference().child('counter').once().then((DataSnapshot snapshot) { print('Connected to second database and read ${snapshot.value}'); }); database.setPersistenceEnabled(true); database.setPersistenceCacheSizeBytes(10000000); _counterRef.keepSynced(true); _counterSubscription = _counterRef.onValue.listen((Event event) { error = null; _counter = event.snapshot.value ?? 0; }, onError: (Object o) { error = o; }); } getData() async { return await FirebaseDatabase.instance.reference().child('counter').limitToFirst(10); } DatabaseError getError() { return error; } int getCounter() { return _counter; } DatabaseReference ref() { return _userRef; } addUser(FileData user) async { final TransactionResult transactionResult = await _counterRef.runTransaction((MutableData mutableData) async { mutableData.value = (mutableData.value ?? 0) + 1; return mutableData; }); if (transactionResult.committed) { _userRef.push().set(<String, String>{ "name": "" +, "image": "" + user.picFile, }).then((_) { print('Transaction committed.'); }); } else { print('Transaction not committed.'); if (transactionResult.error != null) { print(transactionResult.error.message); } } } void deleteUser(FileData user) async { await _userRef.child( { print('Transaction committed.'); }); } void updateUser(FileData user) async { await _userRef.child({ "name": "" +, "image": "" + user.picFile, }).then((_) { print('Transaction committed.'); }); } void dispose() { _messagesSubscription.cancel(); _counterSubscription.cancel(); } }

7. It's time to create Firebase storage API access-able methods. In firebase_storage_util.dart class, we have written method for upload a file on Firebase Storage.
import 'dart:io'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:path/path.dart'; class FirebaseStorageUtil { static final FirebaseStorageUtil _instance = new FirebaseStorageUtil.internal(); FirebaseStorageUtil.internal(); factory FirebaseStorageUtil() { return _instance; } StorageUploadTask uploadFile(File file) { final StorageReference ref = new FirebaseStorage().ref().child('Files').child(basename(file.path)); final StorageUploadTask uploadTask = ref.putFile(file); return uploadTask; } }
8. At the end create a PODO filedata.dart file. It'll keep information on uploaded file that we using for display gallery list.
import 'package:firebase_database/firebase_database.dart';  
class FileData { String _id; String _name; String _file; FileData(this._id,this._name, this._file); String get name => _name; String get picFile => _file; String get id => _id; FileData.fromSnapshot(DataSnapshot snapshot) { _id = snapshot.key; _name = snapshot.value['name']; _file = snapshot.value['image']; } }

now sync the project and run it. If you facing any problem to implement it. Please check working source code from below link and you can quickly check interface and feature with the help of Android APK that is given here:

Source Code               Flutter firebse storage apk

If you have followed the article carefully, you can see the app running very smoothly as shown in the above video. But if you are facing any problem or you have any quires, please feel free to ask it from below comment section and you can ask it on SLACK.


Get it on Google Play

Flutter - Expendable list with ExpansionTile and ExpansionPanel

In mobile application, if you want to show items in a list with expandable feature. The expandable view is a way that show items in a vertic...