如何解决Flutter + Firestore 聊天 .. Listview 重建所有项目
我正在尝试使用 Flutter 和 Firestore 向我的应用添加聊天功能(有点类似于 WhatsApp 功能)。 Firestore 的主要结构是有 2 个集合(我也想要未读消息数):
- users:每个用户都有一个包含所有 CHATS_ID 的子集合“聊天”。这将是通过获取用户聊天列表来构建主页聊天页面(显示所有聊天的历史列表)的主要位置。
- 聊天:所有聊天的列表,每个聊天文档都有一个消息子集。
我的主要问题是构建主页(应显示所有用户以前聊天的列表)。我获取/订阅用户聊天子集合,并且对于其中列出的每个聊天 ID,我还订阅聊天集合中的聊天本身(使用 ID)。
以下是原理截图:
用户集合:
聊天收集:
我正在做的是检索用户的聊天子集合(并使用 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());
});
}
搜索后,我找到了这些相关的建议(但都不起作用):
- 一个 github 问题建议,因为流是可重新排序的(github:[https://github.com/Flutter/Flutter/issues/58917]),但即使使用带有委托和 findChildindexCallback 的 ListView.custom,也一样问题仍然存在。
- 使用distinct。
但是删除内部流构建器并在没有订阅的情况下仅返回图块,会使 ListView.builder 按预期工作(仅构建查看的图块)。所以我的问题是:
- 为什么嵌套流构建器会导致重新构建所有项目。
- 是否有更好的结构来实现上述功能(所有带有未读计数和最后一条消息/时间的实时聊天)。特别是我还没有添加延迟加载。此外,使用这种设计,我必须为每条消息更新多个文档(在聊天集合和每个用户的子集合中)。
您的帮助将不胜感激(我已经查看了一些其他 SO 主题和中等文章,但找不到将这些功能与使用 Firestore 和 Flutter 的可扩展性/价格优化设计结合在一处的文章)。
解决方法
我认为你可以这样做:
Widget build(ctx) {
return ListView.builder(
itemCount: snapshot.data.size,itemBuilder: (index,ctx) =>_catche[index],)
}
对于_catch:
List<Widget> _catche = [/*...*/];
// initialize on load
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。