0

我正在尝试使用颤振和 Firestore 向我的应用程序添加聊天功能(类似于 WhatsApp 功能)。Firestore 的主要结构是有 2 个集合(我也想要未读消息计数):

  1. users:每个用户都有一个包含所有 CHATS_ID 的子集合“chats”。这将是通过获取用户聊天列表来构建主页聊天页面(显示所有聊天的历史列表)的主要位置。
  2. 聊天:所有聊天的列表,每个聊天文档都有一个消息子集合。

我的主要问题是构建主页(应显示所有用户以前聊天的列表)。我获取/订阅用户聊天子集合,并且对于其中列出的每个聊天 ID,我还订阅聊天集合中的聊天本身(使用 ID)。

以下是它的原则截图:

用户集合:

在此处输入图像描述

聊天收藏:

在此处输入图像描述

这是感兴趣的主屏幕(whatsapp 屏幕的原理): 在此处输入图像描述

我正在做的是检索用户的聊天子集合(并使用 StreamBuilder 为其注册一个监听器),以及未读消息/最后一条消息和最后一条消息时间的数量,我订阅以收听这些聊天中的每一个(并希望使用每个用户的最后一条消息时间、状态和他在该聊天文档中的最后一次出现来计算未读计数)。

问题是 Listview.builder 重建所有项目(最初和滚动时),而不仅仅是查看的项目。这是我的代码:

  Stream<QuerySnapshot> getCurrentUserChats(userId) {
    return FirebaseFirestore.instance
        .collection(AppConstants.USERS_COLLECTION)
        .doc('$userId')
        .collection(AppConstants.USER_CHATS_SUBCOLLECTION)
        .orderBy('lastMsgTS', descending: true)
        .snapshots()
        .distinct();
  }

  Widget getRecentChats(userId) {
    return StreamBuilder<QuerySnapshot>(
        stream: getCurrentUserChats(userId),
        builder: (context, snapshot) {
          if (snapshot.hasData && snapshot.data.docs.isNotEmpty) {
            print('snapshot of user chats subcoll has changed');
            List<QueryDocumentSnapshot> retrievedDocs = snapshot.data.docs;
            return Container(
              height: 400,
              child: ListView.builder(
                //childrenDelegate: SliverChildBuilderDelegate(
                itemCount: snapshot.data.size,
                itemBuilder: (context, index) {
                  String chatId = retrievedDocs[index].id;
                  print('building index: $index, chatId: $chatId');

                  return StreamBuilder(
                    stream: FirebaseFirestore.instance
                        .collection(AppConstants.CHATS_COLLECTION)
                        .doc('$chatId')
                        .snapshots()
                        .distinct(),
                    builder:
                        (context, AsyncSnapshot<DocumentSnapshot> snapshot) {

                      if (snapshot.hasData) {
                        print('${snapshot.data?.id}, isExist: ${snapshot.data?.exists}');
                        if (snapshot.data.exists) {
                          return KeyProxy(
                            key: ValueKey(chatId),
                            child: ListTile(
                              leading: CircleAvatar(
                                child: Container(
                                  //to be replaced with user image
                                  color: Colors.red,
                                ),
                              ),
                              title: Text('$chatId'),
                              subtitle: Text(
                                  "Last Message received on: ${DateTimeUtils.getDateViewFromDT(snapshot.data.data()['ts']?.toDate())}"),
                            ),
                          );
                        }
                      }

                      return SizedBox.shrink();
                    },
                  );
                },
                /*childCount: snapshot.data.size,
                      findChildIndexCallback: (Key key) {
                        print('calling findChildIndexCallback');
                        final ValueKey valKey = key;
                        final String docId = valKey.value;
                        int idx = retrievedDocs.indexOf(retrievedDocs
                            .where((element) => element.id == docId)
                            .toList()[0]);
                        print('docId: $docId, idx: $idx');
                        return idx;
                      }*/
              ),
            );
          }

          return Center(child: UIWidgetUtils.loader());
        });
  }

经过搜索,我找到了这些相关的建议(但都没有工作):

  1. 一个 github 问题表明该流是可重新排序的(github:[https://github.com/flutter/flutter/issues/58917]),但即使将 ListView.custom 与委托和 findChildIndexCallback 一起使用,同样的问题仍然存在。
  2. 使用不同的。

但是删除内部流构建器并仅在没有订阅的情况下返回图块,使 ListView.builder 按预期工作(仅构建查看的图块)。所以我的问题是:

  1. 为什么有嵌套的流构建器会导致所有项目都被重新构建。
  2. 是否有更好的结构来实现上述功能(所有聊天都具有未读计数和实时最后一条消息/时间)。特别是我还没有添加延迟加载。而且使用这种设计,我必须为每条消息更新多个文档(在聊天集合和每个用户的子集合中)。

您的帮助将不胜感激(我已经检查了一些其他 SO 线程和中型文章,但找不到将这些功能结合在一个地方的文章,最好是使用 Firestore 和 Flutter 为可扩展性/价格优化设计)。

4

1 回答 1

0

我认为你可以这样做:

  Widget build(ctx) {
    return ListView.builder(
      itemCount: snapshot.data.size,
      itemBuilder: (index, ctx) =>_catche[index],
    )
  }

对于 _catch:

  List<Widget> _catche = [/*...*/];
  // initialize on load
于 2021-06-08T12:41:31.190 回答