0

I am new at Flutter so I am sorry if this problem seems trivial or irrelevant!

I have created another class for getting and setting data, Repository, as I use Cloud Firestore, the data I want for this specific question is stored in a collection, so I get all the documents in the collection as a QuerySnapshot and then add all the documents in a List<DocumentSnapshot>.

Here is the method:

Future<List<DocumentSnapshot>> getCollection(CollectionReference colRef) async {
    List<DocumentSnapshot> dummyList = new List();
    await colRef.getDocuments().then((value) {
      dummyList.addAll(value.documents);
    });
    return dummyList;
  }

Repository:

CollectionReference memoriesColRef =
    _firestore
        .collection("username")
        .document("memories")
        .collection("allMem");

    List<DocumentSnapshot> documentList = new List();
    await getCollection(memoriesColRef).then((value) {
      documentList.addAll(value);
    });

After all this, I have set up a method in my UI class, to call this Repository, and it works perfectly there bet when I call it in the build function, the global list I have passed to access the data, is not able to add the values in it

UI Class

build(...) {
  getMemories().then((value) {
      print("value size: " + value.length.toString()); // 1
      memoriesListMap.addAll(value); // global variable
    });
  print("valSize: " + memoriesListMap.length.toString()); // 0
  print("val: " + memoriesListMap[0]["title"]); // error line
}

Future<List<Map<String, dynamic>>> getMemories() async{
    List<Map<String, dynamic>> dummyListMap = new List();

    await MemoryOper().getMemories().then((value) {
      print("memVal: " + value[0]["title"]); // appropriate value
      dummyListMap.addAll(value);
    });
    return dummyListMap;
  }

ERROR RangeError (index): Invalid value: Valid value range is empty: 0\

I don't know what's causing this, but please help me out! Thank you

EDIT:

ListView.builder(
                itemBuilder: (BuildContext context, int index) {
                  String title = memoriesListMap[index]["title"]; // error prone line
                  int remind = memoriesListMap[index]["remind"];
                  String link = memoriesListMap[index]["link"];
Shlok Jain
  • 343
  • 8
  • 17
  • Does this answer your question? [What is a Future and how do I use it?](https://stackoverflow.com/questions/63017280/what-is-a-future-and-how-do-i-use-it) – nvoigt Aug 20 '20 at 17:33
  • Pay attention to the "How do I use it" part. You need a `FutureBuilder` if you want to build something that depends on the result of a Future. – nvoigt Aug 20 '20 at 17:35
  • As a personal side note, try to not mix `.then()`and `await`. For someone new to Futures and async/await it can be very confusing. There is nothing wrong with using both together once you know your way around them, but for starters, it's easier if you pick one of them. I suggest `await`, it is harder to make mistakes like this that way. – nvoigt Aug 20 '20 at 17:37
  • so ideally, if I follow that answer, I should be able to form a solution right? – Shlok Jain Aug 20 '20 at 17:40
  • Yes, indeed. Whether it's a hypothetical pizza delivery or loading something from firestore, the concept of "I get my data later, what do I do?" is always the same. – nvoigt Aug 20 '20 at 17:41
  • okay thanks a lot! you can answer on the question so that I can close this question – Shlok Jain Aug 20 '20 at 17:50
  • @nvoigt, solved the problem through a FutureBuilder! Thank you! – Shlok Jain Aug 21 '20 at 05:39

2 Answers2

1

I addition to what nvoigt has said, This article will help you to understand how to implement the Future Builder, in your specific case you can do something like:

build(...){
..
body: getMemories(),
..
}

Widget getMemories() {
  return FutureBuilder(
    builder: (context, projectSnap) {
      if (projectSnap.connectionState == ConnectionState.none &&
          projectSnap.hasData == null) {
        return Container();
      }
      return ListView.builder(
        itemCount: projectSnap.data.length,
        itemBuilder: (context, index) {
          ProjectModel project = projectSnap.data[index];
          return Column(
            children: <Widget>[
              // Widget to display the list of project
            ],
          );
        },
      );
    },
    future: getCollection(), //this is the important part where you get the data from your Future
  );
}

Emmanuel
  • 1,436
  • 1
  • 11
  • 17
0

I think you are accessing the element before it gets added since it is async method.
Try something like this,

build(...) {
  getMemories().then((value) {
      print("value size: " + value.length.toString()); // 1
      memoriesListMap.addAll(value); // global variable
      print("valSize: " + memoriesListMap.length.toString()); // 0
      print("val: " + memoriesListMap[0]["title"]); 
   });
}

Hope it works!

Shri Hari L
  • 4,551
  • 2
  • 6
  • 18
  • yes, this will work, but I have to use that variable outside of the `.then()`, if you want, I can post the code, where I am using it – Shlok Jain Aug 20 '20 at 17:23
  • Yes, please post it. If you have to use it, then you could wait until `async` function gets executed. – Shri Hari L Aug 20 '20 at 17:35
  • This won't work. If you need the results to build, you need to wait for the results, not use `.then()`. Since the build method cannot be async, you can't. So you need another way. This is normally handled by using a FutureBuilder, see my duplicate vote. – nvoigt Aug 20 '20 at 17:39