Flutter中Firestore未读消息数Stream数据先显后空问题排查
问题:Firestore聊天应用未读消息数量初始显示后立即变为null
在开发基于Firebase Firestore的聊天应用时,尝试在聊天列表中展示未读消息数量,初始能正确显示数字,但随后数据立刻变为null。
相关代码
聊天列表StreamBuilder代码
body: StreamBuilder( stream: FirebaseFirestore.instance .collection('chatRooms') .where('emails', arrayContainsAny: [user?.email]).snapshots(), builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) { var chatLists = snapshot.data?.docs; if (snapshot.hasError) { return Text('Something error...'); } if (snapshot.connectionState == ConnectionState.waiting) { return Text('is Loading...'); } return ListView.builder( itemCount: chatLists?.length, itemBuilder: (context, index) { if (chatLists?[index]['currentMsg'] != null && chatLists?[index]['currentMsg'] != "") { var list = List.from(chatLists?[index]['members']); var member = ''; if (loginUser['userName'] != null) { for (int i = 0; i < list.length; i++) { if (list[i] != loginUser['userName']) { member += list[i]; } } } return ListTile( title: Row( children: [ Text(member), const SizedBox( width: 20.0, ), ChatLength(docId: chatLists![index].id, uid: user!.uid), ], ), subtitle: SizedBox( height: 40.0, child: Text( chatLists[index]['currentMsg'], overflow: TextOverflow.ellipsis, )), trailing: Text( tmFormmater(chatLists?[index]['currentTm']), style: const TextStyle( color: Color(0xff999999), ), ), onTap: () => Navigator.push( context, MaterialPageRoute( builder: (context) => ChatScreen( docId: chatLists![index].id, title: member, ), )), ); } else { return Container(); } }, ); return Container(); }, ),
ChatLength组件代码
class ChatLength extends StatelessWidget { const ChatLength({super.key, required this.docId, required this.uid}); final String docId; final String uid; @override Widget build(BuildContext context) { return StreamBuilder( stream: FirebaseFirestore.instance .collection('message_read') .doc(docId) .collection('message') .where('userId', isNotEqualTo: uid) .where('read', isEqualTo: false) .snapshots(), builder: (context, snapshot) { print(snapshot.data?.size); if (snapshot.data?.size != null) { return Container( child: Text('${snapshot.data?.size}'), ); } else { return Container(); } }, ); } }
调试日志
===== Debug console ===== flutter: 1 flutter: null // Data changes to null immediately
Firestore集合结构
message_read -> documentId -> message -> documentId -> field(userId(chat writer), read)
问题原因
ChatLength组件中的StreamBuilder没有正确处理快照的状态:
- 当Firestore的stream首次返回数据时,
snapshot.data?.size能获取到有效值; - 当stream触发更新(比如集合数据变化、连接状态波动)时,会先返回一个
snapshot.data为null的中间状态,此时直接判断snapshot.data?.size != null会返回false,导致显示空Container; - 没有处理连接等待状态和错误状态,加剧了数据显示的不稳定。
解决方案
修改ChatLength组件的StreamBuilder,完善状态判断逻辑,确保只在数据有效时显示未读数量:
class ChatLength extends StatelessWidget { const ChatLength({super.key, required this.docId, required this.uid}); final String docId; final String uid; @override Widget build(BuildContext context) { return StreamBuilder<QuerySnapshot>( stream: FirebaseFirestore.instance .collection('message_read') .doc(docId) .collection('message') .where('userId', isNotEqualTo: uid) .where('read', isEqualTo: false) .snapshots(), builder: (context, snapshot) { // 处理错误状态 if (snapshot.hasError) { return const SizedBox(); } // 处理加载状态 if (snapshot.connectionState == ConnectionState.waiting) { return const SizedBox(); } // 确保数据存在再获取数量 final unreadCount = snapshot.data?.size ?? 0; print(unreadCount); // 只有未读数量大于0时才显示 return unreadCount > 0 ? Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Colors.red, borderRadius: BorderRadius.circular(10), ), child: Text( '$unreadCount', style: const TextStyle(color: Colors.white, fontSize: 12), ), ) : const SizedBox(); }, ); } }
优化点说明
- 明确指定StreamBuilder的泛型为
QuerySnapshot,避免类型模糊; - 增加错误状态判断,出错时返回空容器;
- 增加加载状态判断,等待时返回空容器;
- 使用
??运算符给未读数量设置默认值0,避免null; - 仅当未读数量大于0时才显示数字,符合聊天应用的常规交互;
- 给数字容器添加样式,提升视觉效果。
内容的提问来源于stack exchange,提问作者user20455755
相关产品推荐
相关产品推荐

