3

Here is my upload function. My desired output is a list of all urls, but it returns an empty list. I have tried different suggested solutions, but all of them have failed.

Future<List<String>> uploadFiles(List _images) async {
  List<String> imagesUrls=[];

   _images.forEach((_image) async{
    StorageReference storageReference = FirebaseStorage.instance
        .ref()
        .child('posts/${_image.path}');
    StorageUploadTask uploadTask = storageReference.putFile(_image);
    await uploadTask.onComplete;

     imagesUrls.add(await storageReference.getDownloadURL());
     
  });
print(imagesUrls);
return imagesUrls;
}
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
Bright
  • 707
  • 2
  • 10
  • 22

5 Answers5

16

I think you'll need Future.wait to ensure all futures are resolved before continuing:

Future<List<String>> uploadFiles(List<File> _images) async {
  var imageUrls = await Future.wait(_images.map((_image) => uploadFile(_image)));
  print(imageUrls);
  return imageUrls;
}

Future<String> uploadFile(File _image) async {
  StorageReference storageReference = FirebaseStorage.instance
      .ref()
      .child('posts/${_image.path}');
  StorageUploadTask uploadTask = storageReference.putFile(_image);
  await uploadTask.onComplete;

  return await storageReference.getDownloadURL();
}
Henok
  • 3,293
  • 4
  • 15
  • 36
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • I haven't quite understood how to implement your functions. Do i use two functions? – Bright Aug 20 '20 at 21:48
  • @Bright `uploadFile` is uploading and getting the download URL of a single file while `uploadFiles` is doing it with multiple. Frank just extracted the looped part of your original code to its own function. – Christopher Moore Aug 20 '20 at 22:06
  • @Frank `uploadFiles` doesn't need to be marked `async` and `uploadFile` does. – Christopher Moore Aug 20 '20 at 22:07
  • @ChristopherMoore In the UploadFiles, why did you use `uploads.add()`. – Bright Aug 20 '20 at 22:19
  • @Bright **I** did not. I am not the writer of this post. Looks like something that was just left in when it was copied from your original. Removing it and the `toList` looks like it should work. – Christopher Moore Aug 20 '20 at 22:21
  • Sorry, i meant @Frank – Bright Aug 20 '20 at 22:23
  • 1) @ChristopherMoore Good catch on both cases of `async`. I actually forgot an `await` in the `uploadFiles`, so I think that means it's `async` too right? 2) Also good point on the copy/paste forgot to modify. That explains why my parentheses didn't match up. 3) Ah, I didn't notice that `Future.wait` takes an `Iterable`. That's indeed another nice cleanup. – Frank van Puffelen Aug 20 '20 at 23:28
3

this not working. It will direct return without waiting for the foreach loop to be finished. You may use the code below:

Future<List<String>> uploadImages(List<File> images) async {
if (images.length < 1) return null;

List<String> _downloadUrls = [];

await Future.forEach(images, (image) async {
  Reference ref = FirebaseStorage.instance
      .ref()
      .child('jobPost')
      .child(getFileName(path: image));
  final UploadTask uploadTask = ref.putFile(image);
  final TaskSnapshot taskSnapshot = await uploadTask.whenComplete(() {});
  final url = await taskSnapshot.ref.getDownloadURL();
  _downloadUrls.add(url);
});

return _downloadUrls;
}

You may visit this to know what's the reason.

liow
  • 51
  • 3
2

Try this

Future<List<String>> uploadFiles(List _images) async {
  List<String> imagesUrls=[];

   _images.forEach((_image) async{
    StorageReference storageReference = FirebaseStorage.instance
        .ref()
        .child('posts/${_image.path}');
    StorageUploadTask uploadTask = storageReference.putFile(_image);
    

     imagesUrls.add(await (await uploadTask.onComplete).ref.getDownloadURL());
     
  });
print(imagesUrls);
return imagesUrls;
}

How to use this:

List<String> urls = Future.wait(uploadFiles(_images));
thien nguyen
  • 306
  • 5
  • 10
  • 1
    The usage should be ```List urls = await uploadFiles(_images);``` The ```uploadFiles``` does not return a list of futures but a future of a list. If you wait for it to complete, you will just get the list of download urls. – Yacin May 03 '21 at 23:34
1

There is complete example you can get the idea

import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:firebase_storage/firebase_storage.dart' as firebase_storage;
import 'package:path/path.dart' as Path;

class AddImage extends StatefulWidget {
  @override
  _AddImageState createState() => _AddImageState();
}

class _AddImageState extends State<AddImage> {
  bool uploading = false;
  double val = 0;
  CollectionReference imgRef;
  firebase_storage.Reference ref;

  List<File> _image = [];
  final picker = ImagePicker();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Add Image'),
          actions: [
            FlatButton(
                onPressed: () {
                  setState(() {
                    uploading = true;
                  });
                  uploadFile().whenComplete(() => Navigator.of(context).pop());
                },
                child: Text(
                  'upload',
                  style: TextStyle(color: Colors.white),
                ))
          ],
        ),
        body: Stack(
          children: [
            Container(
              padding: EdgeInsets.all(4),
              child: GridView.builder(
                  itemCount: _image.length + 1,
                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: 3),
                  itemBuilder: (context, index) {
                    return index == 0
                        ? Center(
                            child: IconButton(
                                icon: Icon(Icons.add),
                                onPressed: () =>
                                    !uploading ? chooseImage() : null),
                          )
                        : Container(
                            margin: EdgeInsets.all(3),
                            decoration: BoxDecoration(
                                image: DecorationImage(
                                    image: FileImage(_image[index - 1]),
                                    fit: BoxFit.cover)),
                          );
                  }),
            ),
            uploading
                ? Center(
                    child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Container(
                        child: Text(
                          'uploading...',
                          style: TextStyle(fontSize: 20),
                        ),
                      ),
                      SizedBox(
                        height: 10,
                      ),
                      CircularProgressIndicator(
                        value: val,
                        valueColor: AlwaysStoppedAnimation<Color>(Colors.green),
                      )
                    ],
                  ))
                : Container(),
          ],
        ));
  }

  chooseImage() async {
    final pickedFile = await picker.getImage(source: ImageSource.gallery);
    setState(() {
      _image.add(File(pickedFile?.path));
    });
    if (pickedFile.path == null) retrieveLostData();
  }

  Future<void> retrieveLostData() async {
    final LostData response = await picker.getLostData();
    if (response.isEmpty) {
      return;
    }
    if (response.file != null) {
      setState(() {
        _image.add(File(response.file.path));
      });
    } else {
      print(response.file);
    }
  }

  Future uploadFile() async {
    int i = 1;

    for (var img in _image) {
      setState(() {
        val = i / _image.length;
      });
      ref = firebase_storage.FirebaseStorage.instance
          .ref()
          .child('images/${Path.basename(img.path)}');
      await ref.putFile(img).whenComplete(() async {
        await ref.getDownloadURL().then((value) {
          imgRef.add({'url': value});
          i++;
        });
      });
    }
  }

  @override
  void initState() {
    super.initState();
    imgRef = FirebaseFirestore.instance.collection('imageURLs');
  }
}
samad shukr
  • 561
  • 7
  • 15
0

Future uploadFile() async { int i = 1;

for (var img in _image) {
  setState(() {
    val = i / _image.length;
  });