0

I am passing my constructor list of Firestore Document Ids and I want to retrieve and show data belonging to those ids only in my 'LinksData' collection.

So basically I want to retrieve the Document snapshot of all documents belonging to id's that are in another list of mine (which I am passing in the constructor).

I could think of two methods :

  1. Using StreamBuilder, but according to what i know, with this i will either get all the documents snapshots in my collection or just one.
  2. Using Future Builder, i made this following function but when i use it , it shows the following error
[ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: Bad state: field does not exist within the DocumentSnapshotPlatform

How can i do this? I don't want to retrieve all the documents using stream builder, because that would affect my daily reads.

Here is the code of how i tried using FutureBuilder.

FutureBuilder<List<LinkCards>>(
                future: generateList(widget.linksDataIds),
                builder: (context, snapshot) {
                  if (snapshot.hasError) {
                    return Center(child: Text('Oops! Some Error Occurred!'));
                  } else {
                    if (!snapshot.hasData) {
                      return Center(child: CircularProgressIndicator());
                    }
                    return GridView.builder(
                        gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
                          maxCrossAxisExtent: 220,
                          mainAxisSpacing: 12.0,
                        ),
                        itemCount: snapshot.data!.length,
                        itemBuilder: (context, index) {
                          return snapshot.data![index];
                        });
                  }
                },
              )

Here is my Future fucntion

  Future<List<LinkCards>> generateList(List<dynamic> listLinksDataId) async {
    FirebaseFirestore fireStore = FirebaseFirestore.instance;
    List<LinkCards> listCards = [];

    listLinksDataId.forEach((linkDataId) async {
      print(linkDataId);

      await fireStore
          .collection('LinksData')
          .doc(linkDataId)
          .get()
          .then((value) => {
                listCards.add(LinkCards(
                  link: value.get('link'),
                  linkTitle: value.get('title'),
                  linkImage: value.get('image'),
                  relatedCategories: value.get('categories'),
                  platform: value.get('platform'),
                ))
              });
    });
    return listCards;
  }

Here is an image of how my database is structured : enter image description here

3 Answers3

0

This how your function should ideally look like. Give it a try, and hopefully it will work.

Future<List<LinkCards>> generateList(List<dynamic> listLinksDataId) async {
    FirebaseFirestore fireStore = FirebaseFirestore.instance;
    List<LinkCards> listCards = [];
    for(int i=0; i<listLinksDataId.length;i++){

       final firestoreResult = await fireStore
          .collection('LinksData')
          .doc(linkDataId)
          .get();
       listCards.add(
        link: firestoreResult.data()["link"],
        linkTitle: firestoreResult.data()["title"],
        linkImage: firestoreResult.data()["image"],
        relatedCategories: firestoreResult.data()["categories"],
        platform: firestoreResult.data()["platform"],
       );

    }
    return listCards;
  }

This is the approach I usually go for, and hopefully will be useful for you too.

Aashar Wahla
  • 2,785
  • 1
  • 13
  • 19
  • With this, control goes into snapshot.hasError , and on further printing firestoreResult.data()["link"], it still gives null., even though it is there in the database. – Aamir Zaidi May 31 '21 at 19:18
  • The error is because this function doesn't know what `linkDataId` is. In the original question it was from the `forEach` loop but in this one with the regular for loop `linkDataId` doesn't exist at all. – Loren.A May 31 '21 at 22:40
0

This should get you pretty close. It's a simplified example that I can reproduce that just uses a list of Strings.

Future<List<String>> generateList(List<String> listLinksDataId) async {
    List<String> listCards = [];
    final collection = FirebaseFirestore.instance.collection('LinksData');
    for (String docId in listLinksDataId) {
      final snapshot = await collection.doc(docId).get(); 

     // snapshot is the document snapshot whose id 
     // matches the index in the list you're passing in

      final linksData = snapshot['linksData']; // list of ids in linksData field

      for (var i = 0; i < linksData.length; i++) { // looping through list
        final field = linksData[i];
        listCards.add(field);
      }
    }
    return listCards;
  }

This will at the very least return all of the id's in the linksData field only in the documents that match an index of the list you pass in.

You should be able to setup a quick test page and a FutureBuilder<ListString>> and pass in this method as the Future.

Return a ListView that just displays text widgets and you'll see all the ids that you want.

Expanded(
   child: ListView.builder(
      itemCount: snapshot.data!.length,
      itemBuilder: (context, index) =>
         Text(snapshot.data![index])),
   );

Then you can modify from there to fully populate your LinkCard.

Loren.A
  • 4,872
  • 1
  • 10
  • 17
0

Thanks for your replies, but i found a solution by modifying my database a little bit. In 'LinksData' collection, I put a field (type array) which will contain all the categories. Then i used the StreamBuilder instead like this :

StreamBuilder<QuerySnapshot>(
                stream: FirebaseFirestore.instance
                    .collection('LinksData')
                    .where('categories', arrayContains: widget.categoryName)
                    .snapshots(),

                builder: (context, snapshot) {
                  if (snapshot.hasError) {
                    return Center(child: Text('${snapshot.error}'));
                  } else {
                    if (!snapshot.hasData) {
                      return Center(child: CircularProgressIndicator());
                    }
                    return GridView.builder(
                        gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
                          maxCrossAxisExtent: 220,
                          mainAxisSpacing: 12.0,
                        ),
                        itemCount: snapshot.data!.docs.length,
                        itemBuilder: (context, index) {
                          return LinkCards(
                              linkImage: snapshot.data!.docs[index].get('image'),
                              linkTitle: snapshot.data!.docs[index].get('name'),
                              link: snapshot.data!.docs[index].get('link'),
                              relatedCategories: snapshot.data!.docs[index].get('categories'),
                              platform: snapshot.data!.docs[index].get('platform'));
                        });
                  }
                },
              )