0
votes

I'm writing a web app with Flutter (Flutter web available on beta channel), and the official, Google supported, Firebase library doesn't support the web version of flutter.

I'm using an open source plugin (which has been incredible), acting as a wrapper for the JSON end point functionality of Firebase. Plugin: https://pub.dev/packages/firebase

Future<List<Item>> pullItems(int zip, String key, int index) async {
    List<Item> itemList = [];
    final ref = store.collection("vendors-global").doc("usa").collection(zip.toString()).doc(key).collection("items");
    int i = 0;
    ref.onSnapshot.listen((querySnapshot) {
      final int snapSize = querySnapshot.size;
    querySnapshot.forEach((field) {
      var item = field.data();
      print(key + " : " + field.id);     //this prints all the items in ea. key, for debugg
      Item itemObject = new Item(item['name']);
      itemList.add(itemObject);
      i++;
      if (i == snapSize) { //this counter is how I triggered this function off after a
                     //similar function before it finished, where the parent keys came from.
        return itemList;
      }
      });
    });
  return itemList;
}

The issue seems to be with the .onSnapshot.listen((querySnapshot), specifically the .listen, being an open connection. I've seen this done where the list of Items is defined in a one line call to the Firestore beginning with an await statement, and ending with .map(()..).toList(). But it was with the standard google API which seems to have some more flexibility.

Future<List<Item>> loadItems() async {
      final List<Item> itemList = (await Firestore.instance.collection('items').getDocuments()).documents.map((snapshot) => Item(doc['kode'], doc['name'])).toList();
      return itemList;
    }

The practical issue is that this future function immediately returns the blank List itemList = []; due to the return at the bottom. However, if I remove the bottom return it throws a warning and won't compute at that point.

Is there any way to use the .onSnapshot listen functionality to traverse through a number of docs in a collection, and return them as a list?

Thanks so much for any input, this has been frustrating me for 10+ hours

1
The return statement inside the for each isn't doing what you expect. It's just returning from the closure function you are passing as the parameter of the for each, not the enclosing function. Refactor the for each as a normal for loop like for (var field in querySnapshot) { and the return statement will then work as expected. - Richard Heap

1 Answers

0
votes

What you need is to use a Completer ... This works just like a promise deferal in javascript if you are familiar with that. Check it out:

Future<List<Item>> pullItems(int zip, String key, int index) {
    Completer<List<Item>> completer = Completer();
    List<Item> itemList = [];
    final ref = store.collection("vendors-global").doc("usa").collection(zip.toString()).doc(key).collection("items");
    int i = 0;
    ref.onSnapshot.listen((querySnapshot) {
      final int snapSize = querySnapshot.size;
    querySnapshot.forEach((field) {
      var item = field.data();
      print(key + " : " + field.id);     //this prints all the items in ea. key, for debugg
      Item itemObject = new Item(item['name']);
      itemList.add(itemObject);
      i++;
      if (i == snapSize) { //this counter is how I triggered this function off after a
                     //similar function before it finished, where the parent keys came from.
        completer.complete(itemList);
      }
      });
    });
  return completer.future;
}

Notice how I removed the async from the end of your function .. the async is there to automatically create the Future<List<Item>> for you if there are any await calls used inside of the function. Since you are using callbacks instead of await then you should just use the Completer to handle your future for you. It returns the future and will trigger any code waiting on its "completion" when completer.complete(...) is called.