How to query firestore document inside streambuilder and update the listview

前端 未结 2 867
借酒劲吻你
借酒劲吻你 2021-02-02 01:18

I\'m trying to retrieve posts from a Firestore collection called \"posts\", which contains the post creator\'s userID and post description and this is possible

2条回答
  •  生来不讨喜
    2021-02-02 01:43

    Posting for those in the future since I spent several hours trying to figure this out - hoping it saves someone else.

    First I recommend reading up on Streams: https://www.dartlang.org/tutorials/language/streams This will help a bit and its a short read

    The natural thought is to have a nested StreamBuilder inside the outer StreamBuilder, which is fine if the size of the ListView wont change as a result of the inner StreamBuilder receiving data. You can create a container with a fixed size when you dont have data, then render the data-rich widget when its ready. In my case, I wanted to create a Card for each document in both the "outer" collection and the "inner" collection. For example, I have a a Group collection and each Group has Users. I wanted a view like this:

    [
      Group_A header card,
      Group_A's User_1 card,
      Group_A's User_2 card,
      Group_B header card,
      Group_B's User_1 card,
      Group_B's User_2 card,
    ]
    

    The nested StreamBuilder approach rendered the data, but scrolling the ListView.builder was an issue. When scrolling, i'm guessing the height was calculated as (group_header_card_height + inner_listview_no_data_height). When data was received by the inner ListView, it expanded the list height to fit and the scroll jerks. Its not acceptable UX.

    Key points for the solution:

    • All data should be acquired before StreamBuilder's builder execution. That means your Stream needs to contain data from both collections
    • Although Stream can hold multiple items, you want a Stream>. Comments on this answer (https://stackoverflow.com/a/53903960/608347) helped

    The approach I took was basically

    1. Create stream of group-to-userList pairs

      a. Query for groups

      b. For each group, get appropriate userList

      c. Return a List of custom objects wrapping each pair

    2. StreamBuilder as normal, but on group-to-userList objects instead of QuerySnapshots

    What it might look like

    The compound helper object:

    class GroupWithUsers {
      final Group group;
      final List users;
    
      GroupWithUsers(this.group, this.users);
    }
    

    The StreamBuilder

        Stream> stream = Firestore.instance
            .collection(GROUP_COLLECTION_NAME)
            .orderBy('createdAt', descending: true)
            .snapshots()
            .asyncMap((QuerySnapshot groupSnap) => groupsToPairs(groupSnap));
    
        return StreamBuilder(
            stream: stream,
            builder: (BuildContext c, AsyncSnapshot> snapshot) {
                // build whatever
        });
    

    essentially, "for each group, create a pair" handling all the conversion of types

      Future> groupsToPairs(QuerySnapshot groupSnap) {
        return Future.wait(groupSnap.documents.map((DocumentSnapshot groupDoc) async {
          return await groupToPair(groupDoc);
        }).toList());
      }
    

    Finally, the actual inner query to get Users and building our helper

    Future groupToPair(DocumentSnapshot groupDoc) {
        return Firestore.instance
            .collection(USER_COLLECTION_NAME)
            .where('groupId', isEqualTo: groupDoc.documentID)
            .orderBy('createdAt', descending: false)
            .getDocuments()
            .then((usersSnap) {
          List users = [];
          for (var doc in usersSnap.documents) {
            users.add(User.from(doc));
          }
    
          return GroupWithUser(Group.from(groupDoc), users);
        });
      }
    

提交回复
热议问题