1

I'm attempting to create a listview from data I have in Firestore using FutureBuilder in Flutter but from documentation and other similar questions I cannot seem to make what I'm trying to do work.

I have two collections on Firestore, one that is for users and one for items items.

The users collection contains an array of ids for items.

I want to access the user's item list, get all of the item ids and then user them to get each individual item's data.

Currently I have something that sort of works, but not exactly in the way I want as I cannot sort the list before it creates the ListView, and I know it is by far not optimised.

This is a simplified version of the code I have used to achieve this.

The following code is for accessing Document snapshots from Firestore.

 Future getUserInfo(User user) async{
    // Gets Current user's collection
    DocumentReference userIdRef = usersCollection.document(user.getUid());
    DocumentSnapshot userIdSnapshot = await userIdRef.get();
    return userIdSnapshot.data;
  }
Future getItemInfo(String itemId) async{
    DocumentReference itemIdRef = itemCollection.document(itemId);
    DocumentSnapshot itemIdSnapshot = await itemIdRef.get();
    return itemIdSnapshot.data;
  }

In the Widget tree I have two FutureBuilders, one nested inside the other to create a list. (I've removed details unessessary for the question

class _TestState extends State<Test> {
  FirestoreDatabase firestoreDatabase = new FirestoreDatabase();

  @override
  Widget build(BuildContext context) {
    final user = Provider.of<User>(context);

    return Scaffold(
      body: getUserItemsFromFirestore(user),
    );
  }

  Widget getUserItemsFromFirestore(User user) {
    FirestoreDatabase firestore = new FirestoreDatabase(uid: user.getUid());

    return FutureBuilder(
        future: firestore.getUserInfo(user),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return LoadingSpinner();
          } else if (snapshot.connectionState == ConnectionState.done ||
              snapshot.connectionState == ConnectionState.active) {
            //List<MedicationRegime> medicationList = snapshot.data ?? [];
            return ListView.builder(
              itemCount: snapshot.data['item'].length,
              itemBuilder: (context, index) {
                return getItemList(
                    snapshot.data['item'][index], user, index);
              },
            );
          } else {
            return null;
          }
        });
  }

  Widget getItemList(String itemID, User user, int index) {
    FirestoreDatabase firestore = new FirestoreDatabase(uid: user.getUid());
    return FutureBuilder(
        future: firestore.getItemInfo(itemID),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return LoadingSpinner();
          } else if (snapshot.connectionState == ConnectionState.done ||
              snapshot.connectionState == ConnectionState.active) {
            List itemList = [];
            Item item = new Item(
              itemID: itemID,
              name: snapshot.data['name'],
              type: snapshot.data['type']
            );
              itemList.add(item);
            return ListView.builder(
                physics: NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemCount: itemList.length,
                itemBuilder: (context, index) {
                  return Card(
                    child: ListTile(
                      leading: Icon(itemList[index]
                          .getitemIcon()),
                      title: Text(
                        itemList[index].getName(),
                      ),
                      subtitle: Text(itemList[index].getType()),
                        ],
                      ),
                    );
                  );
                });
          } else {
            return null;
          }
        });
  }
}

Would there be a way to incorporate both of these into the one FutureBuilder, as I need getUserInfo() to have run before I can call getItemInfo so I cannot use Future.wait([getUserInfo, getItemInfo]). Thanks

  • 1
    Try this to get the items' data: https://stackoverflow.com/questions/46721517/google-firestore-how-to-get-document-by-multiple-ids-in-one-round-trip. According to it, you could do either: - myCollection.where(firestore.FieldPath.documentId(), 'in', ["123","456","789"]) (only if the list has no more than 10 items. OR: firestore.getAll(...arrayOfReferences).then() – Andres Silva Jul 28 '20 at 01:20

1 Answers1

1
  return FutureBuilder(
      future:   firestore.getUserInfo(user)
      .then((userInfo)=>firestore.getItemInfo(userInfo.data['itemID'])]),
      builder: (context, itemInfoSnapshot) {

      }

You can chain futures, and the last future that you return will be the one used by future builder.

If you need both data in future builder, you can create your own future.

class CustomResponse {
  String userName;
  String itemName;

  CustomResponse(this.userName,this.itemName);
}

Future<CustomResponse> customFuture() async {
  var userInfo = await firestore.getUserInfo();
  var itemInfo = await firestore.getItemInfo();

  return CustomResponse(userInfo['name'],itemInfo['name']);
}

 FutureBuilder(
      future:   customFuture(),
      builder: (context, customResponse) {

      }

Tree
  • 29,135
  • 24
  • 78
  • 98