DEV Community

Cover image for Flutter Web: Firebase Storage
happyharis
happyharis

Posted on

Flutter Web: Firebase Storage

Flutter Web: Firebase Storage

*Disclaimer: The firebase storage api for Flutter Web is still buggy. There is some uncaught errors in the JS end of the framework. Use at your own risk!

Video tutorial and link for the full code is down below 😁

First, we need to create an upload image button with the function name uploadImage:

// Currently, my app is using a Scaffold widget, hence floatingActionButton

floatingActionButton: FloatingActionButton.extended(
  heroTag: 'picker',
  elevation: 0,
  backgroundColor: Colors.tealAccent[400],
  hoverElevation: 0,
  label: Row(
    children: <Widget>[
      Icon(Icons.file_upload),
      SizedBox(width: 10),
      Text('Upload Image')
    ],
  ),
  onPressed: () => uploadImage(),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,

Second, let's create the function uploadImage(). It's a simple function:

  • a. We need to choose a file that we want to upload
  • b. Once it finishes reading, we then upload it to firebase storage

Currently, flutter web is still in beta. Thus, we have to use the HTML library. I believe the flutter team does not want to use any html library to be consistent with one codebase for multiplatform
IDK, we will see

I got the help from this stackoverflow question.

Thus, we have to create an input element. If we were to refer to the docs about file reader for web in Mozilla doc

It actually gives an example of what we needed:

function previewFile() {
  const preview = document.querySelector("img");
  const file = document.querySelector("input[type=file]").files[0];
  const reader = new FileReader();

  reader.addEventListener(
    "load",
    function() {
      // convert image file to base64 string
      preview.src = reader.result;
    },
    false
  );

  if (file) {
    reader.readAsDataURL(file);
  }
}

Thus, we should convert it to dart:

/// A "select file/folder" window will appear. User will have to choose a file.
/// This file will be then read, and uploaded to firebase storage;
uploadImage() async {
  // HTML input element
  InputElement uploadInput = FileUploadInputElement();
  uploadInput.click();

  uploadInput.onChange.listen(
    (changeEvent) {
      final file = uploadInput.files.first;
      final reader = FileReader();
      // The FileReader object lets web applications asynchronously read the
      // contents of files (or raw data buffers) stored on the user's computer,
      // using File or Blob objects to specify the file or data to read.
      // Source: https://developer.mozilla.org/en-US/docs/Web/API/FileReader

      reader.readAsDataUrl(file);
      // The readAsDataURL method is used to read the contents of the specified Blob or File.
      //  When the read operation is finished, the readyState becomes DONE, and the loadend is
      // triggered. At that time, the result attribute contains the data as a data: URL representing
      // the file's data as a base64 encoded string.
      // Source: https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL

      reader.onLoadEnd.listen(
        // After file finiesh reading and loading, it will be uploaded to firebase storage
        (loadEndEvent) async {
          uploadToFirebase(file);
        },
      );
    },
  );
}

Now, we have to create the function uploadToFirebase() that requires a file.

import 'package:firebase/firebase.dart' as fb;
import 'package:firebase/firestore.dart' as fs;

// In your screen (stateful) widget

// Before your build method, create an UploadTask instance
fb.UploadTask _uploadTask;

/// Upload file to firebase storage and updates [_uploadTask] to the latest
  /// file upload
uploadToFirebase(File imageFile) async {
  final filePath = 'images/${DateTime.now()}.png';
  setState(() {
    _uploadTask = fb
        .storage()
        .refFromURL('YOUR FIREBASE STORAGE URL')
        .child(filePath)
        .put(imageFile);
  });
}

The function above will create a images folder in firebase storage. Moreover, the picture uploaded will be named as time stamps (for reference and simplicity sake).

Lastly, let's create a progress indicator to show the progress of the file being uploaded in a stream builder:

import 'package:firebase/firebase.dart' as fb;

// In your screen widget

StreamBuilder<fb.UploadTaskSnapshot>(
  stream: _uploadTask?.onStateChanged,
  builder: (context, snapshot) {
    final event = snapshot?.data;

    // Default as 0
    double progressPercent = event != null
        ? event.bytesTransferred / event.totalBytes * 100
        : 0;

    if (progressPercent == 100) {
      return Text('Successfully uploaded file 🎊');
    } else if (progressPercent == 0) {
      return SizedBox();
    } else {
      return LinearProgressIndicator(
        value: progressPercent,
      );
    }
  },
),

If you have fast internet, the upload speed is very fast. Here's the flow of the upload process:

  1. User clicks on the button to upload file
  2. The window pops up as input is automatically clicked
  3. Once user has chosen a file, the FileReader will take some micro seconds to read the file
  4. After reading the file, the file will be passed in the uploadToFirebase() function
  5. We will update the the UploadTask instance to the UploadTask instance we just newly created
  6. This will update the progress indicator and show a success message

What is suppose to look like

In the StreamBuilder above, it should look like this:

import 'package:firebase/firebase.dart' as fb;


StreamBuilder<fb.UploadTaskSnapshot>(
  stream: _uploadTask?.onStateChanged,
  builder: (context, snapshot) {
  final event = snapshot?.data;

  switch (event?.state) {
    case fb.TaskState.RUNNING:
      return LinearProgressIndicator(
        value: progressPercent,
      );
    case fb.TaskState.SUCCESS:
      return Text('Success 🎊');

    case fb.TaskState.ERROR:
      return Text('Has error 😢');

    default:
    // Show empty when not uploading
      return SizedBox();
  }
}),

However, there is a bug 🐞, where it returns a null error and type check error in the web console, when you inspect in your browser.

Disclaimer again, do at your own risk.

Github Gist of the firebase storage implementation

Thank you for reading. Hope you have a nice day 😊

Discussion (13)

Collapse
alic50985540 profile image
Alic

Thank you for the tutorial!
It works!
But I have 2 questions:
1) the upload does work from an android browser but not from iPhone. Do you know why?
2) the images, that I upload from android are rotated in firebase. Do you know why?

Thanks!

Collapse
alic50985540 profile image
Alic

I solved the first issue.
The OnChanged does not work with sarafi mobile.

Using Eventlistener is the solution.

Collapse
happyharis profile image
happyharis Author

Sorry I don't. However, thanks for sharing the first issue's solution!

Collapse
iamakx profile image
Akash Giri

I am getting below error

TypeError: Cannot read property 'storage' of undefined
at Object.storage$ as storage
at localhost:54783/packages/dharmacha...
at banner._BannerManagerState.new.setState (localhost:54783/packages/flutter/s...)
at banner._BannerManagerState.new.uploadToFirebase (localhost:54783/packages/dharmacha...)
at uploadToFirebase.next ()
at runBody (localhost:54783/dart_sdk.js:37190:34)
at Object._async as async
at banner._BannerManagerState.new.uploadToFirebase (localhost:54783/packages/dharmacha...)
at banner._BannerManagerState.new. (localhost:54783/packages/dharmacha...)

Collapse
xeandy profile image
xeandy

Hi i saw your video. But image doesnt upload

Collapse
happyharis profile image
happyharis Author

have you set your rules to be able to write without authentications?

Collapse
goksoy66 profile image
goksoy66

same here.
it doesnt make upload.
the rules doesnt require any authentication.
what can it be the reason?

Thread Thread
goksoy66 profile image
goksoy66 • Edited

I found the solution by reading comments below YouTube post.

I have to add the following line to index.html as a script.

gstatic.com/firebasejs/7.11.0/fire...

Collapse
7cold profile image
Léo Coldibelli

downloadURL? How?

Collapse
oscar__martin profile image
Óscar Martin 💙

You saved my night, thanks buddy!

Collapse
svongripp profile image
svongripp

Hey, thanks for the article!

Any idea how to go about streaming FireStore data to a list in Flutter web?

Collapse
happyharis profile image
happyharis Author

Are you getting from a collection or documents?

If collection firestoreData.snapshot.map((data) => data.docs.map((doc)=> doc.data))).toList()

If documents, just add in manually in a list like list.insert(data)

Collapse
zaid13 profile image
zaid bin saeed

TypeError: dart.global.firebase.storage is not a function

when i press enter i see this error