Emanuel Developer
Emanuel Developer

Reputation: 708

Retrieve data with StreamBuilder from collection and subCollection in same time Flutter cloud Firestore

I have a collection called UserCollection, into this collection I have a subcollection with other specific documents called 'UserSubCollection'

with StreamBuilder I am trying to get these collections unsuccesfully

StreamBuilder(
          stream: FirebaseFirestore.instance
              .collection('UserCollection')
          .doc(firebaseUser.uid).collection('UserSubCollection')
              .snapshots(),
          builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
            if (snapshot.hasError)
              return Center(child: Text("Error"));
            else if (snapshot.data.docs.isEmpty) //also check if empty! show loader?
              return Center(child: Text('Errors'));
            else
            return new ListView.builder(
              itemCount: snapshot.data.docs.length,
              itemBuilder: (BuildContext context, int index) {

I am able to get just one or a collection or the subcollection. there is a way to get them both?

Upvotes: 0

Views: 782

Answers (1)

Andrea Costanzo
Andrea Costanzo

Reputation: 2215

First question: do your items need to update automatically over time? Otherwise, I suggest you a FutureBuilder instead of a StreamBuilder.

To get both I suggest you using a separate method like this:

Future<List</*INSERT RETURN TYPE*/>> _retrieveUsers() async{
      //get all the users
      QuerySnapshot query = await FirebaseFirestore.instance
              .collection('UserCollection').get();
      Map<String,Map<String,dynamic>> userMap = Map();
      //add each user to the map
      query.docs.forEach(doc => userMap.putIfAbsent(doc.id, doc.data());
      FutureGroup<QuerySnapshot> queries = FutureGroup(); //add async package in pubspec.yaml [there might be version issues]
      //retrieve sub collections and bind them to the corresponding user
      query.docs.forEach((doc) => queries.add(_retrieveSubCollection(userMap,doc)));
      //Close queries
      queries.close();
      //Await for all the queries to be completed
      await queries.future;
      //now you have for each user a structure containing its data plus everything in its sub collection. Do whatever you want and then return a list or a map according to what you need
      
      return /*YOUR RETURN*/;
 }
 
 Future<Void> _retrieveSubCollection( Map<String,Map<String,dynamic>> userMap, DocumentSnapshot doc) async{
        //retrieve the subcollection of a document using its id
        QuerySnapshot query = query FirebaseFirestore.instance
              .collection('UserCollection')
          .doc(doc.id).collection('UserSubCollection').get();
        //bind the subcollection to the relative user
        Map<String,dynamic> userData = userMap[doc.id];
        userData.add("subcollection", query.docs.map((subDoc) =>subDoc.data()).toList());
        userMap.update(doc.id,(value)=> userData);
        return;
      }

Now just use a future builder to display the result of your queries.

FutureBuilder(future: _retrieveUsers(), builder: (context,snapshot) {/*YOUR CODE*/}

I actually didn't try the code in a real environment, so if you find issues on updating the user map there might be problems due to asynchronous updates. However, since every query target a specific element in the map, I don't think there will be issues

Upvotes: 1

Related Questions