Flutter聊天页面Firestore报错:'isDocument()': is not true 求助
Flutter Firestore Stream 断言错误:'isDocument()': is not true 解决方案
错误原因
chatDocId类型错误:初始化新增聊天文档时,将DocumentReference对象直接赋值给chatDocId,后续调用chatDocId.toString()会生成类似DocumentReference(chats/xxx)的无效字符串,传递给doc()方法时触发Firestore内部断言失败。- 聊天记录查询逻辑缺陷:使用
isEqualTo匹配无序Map对象,因Map键的顺序会影响匹配结果,导致无法正确查询已存在的聊天记录,可能重复创建文档。
修复步骤
1. 修正chatDocId赋值逻辑
新增文档时,提取DocumentReference的id属性作为文档ID,而非直接保存对象:
final newDoc = await chats.add({ "users": {currentUserUid: null, friendUid: null}, }); chatDocId = newDoc.id;
2. 优化聊天记录查询方式
改用多条件查询验证两个用户ID存在于usersMap中,避免Map顺序影响匹配:
final snapshot = await chats .where("users.$currentUserUid", isNull: true) .where("users.$friendUid", isNull: true) .limit(1) .get();
3. 避免无效Stream请求
在chatDocId未初始化完成前,不创建Stream,同时在UI中显示加载状态:
chatDocId == null ? const Center(child: CircularProgressIndicator()) : StreamBuilder<QuerySnapshot>(...)
完整修复代码
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter_chat_bubble/bubble_type.dart'; import 'package:flutter_chat_bubble/chat_bubble.dart'; import 'package:flutter_chat_bubble/clippers/chat_bubble_clipper_6.dart'; class Chat extends StatefulWidget { Chat({super.key, required this.friendUid, required this.friendName}); final String friendUid; final String friendName; @override State<Chat> createState() => _ChatState(friendUid, friendName); } class _ChatState extends State<Chat> { CollectionReference chats = FirebaseFirestore.instance.collection("chats"); final String friendUid; final String friendName; final String currentUserUid = FirebaseAuth.instance.currentUser!.uid; String? chatDocId; final TextEditingController _controller = TextEditingController(); _ChatState(this.friendUid, this.friendName); @override void initState() { super.initState(); _initChatDoc(); } void _initChatDoc() async { final snapshot = await chats .where("users.$currentUserUid", isNull: true) .where("users.$friendUid", isNull: true) .limit(1) .get(); if (snapshot.docs.isNotEmpty) { setState(() { chatDocId = snapshot.docs.single.id; }); } else { final newDoc = await chats.add({ "users": {currentUserUid: null, friendUid: null}, }); setState(() { chatDocId = newDoc.id; }); } } void sendMessage(String msg) { if (msg.isEmpty || chatDocId == null) return; chats.doc(chatDocId).collection("messages").add({ "createdOn": FieldValue.serverTimestamp(), "uid": currentUserUid, "msg": msg }).then((_) { _controller.clear(); }); } bool isSender(String uid) { return uid == currentUserUid; } Alignment getAlignment(String uid) { return isSender(uid) ? Alignment.topRight : Alignment.topLeft; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(friendName)), body: Padding( padding: const EdgeInsets.all(8.0), child: Column( mainAxisSize: MainAxisSize.max, children: [ Expanded( child: chatDocId == null ? const Center(child: CircularProgressIndicator()) : StreamBuilder<QuerySnapshot>( stream: chats .doc(chatDocId) .collection("messages") .orderBy("createdOn", descending: true) .snapshots(), builder: (context, snapshot) { if (snapshot.hasError) { return const Center(child: Text("加载出错")); } if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } final messages = snapshot.data?.docs ?? []; return ListView.builder( reverse: true, itemCount: messages.length, itemBuilder: (context, index) { final data = messages[index].data() as Map<String, dynamic>; final msg = data["msg"] ?? ""; final uid = data["uid"] ?? ""; return ChatBubble( clipper: ChatBubbleClipper6( nipSize: 0, radius: 0, type: isSender(uid) ? BubbleType.sendBubble : BubbleType.receiverBubble, ), alignment: getAlignment(uid), margin: const EdgeInsets.only(top: 20), backGroundColor: isSender(uid) ? const Color(0xFF08C187) : const Color(0xffE7E7ED), child: Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width * 0.7, ), child: Text( msg, style: TextStyle( color: isSender(uid) ? Colors.white : Colors.black, ), maxLines: 100, overflow: TextOverflow.ellipsis, ), ), ); }, ); }, ), ), Row( children: [ Expanded( child: TextField( controller: _controller, decoration: const InputDecoration( hintText: "输入消息...", border: OutlineInputBorder(), ), ), ), IconButton( onPressed: () => sendMessage(_controller.text.trim()), icon: const Icon(Icons.send), ), ], ), ], ), ), ); } }
额外优化建议
- 将
users字段改为List<String>数组类型,查询时使用arrayContains结合逻辑判断,更符合Firestore最佳实践,彻底避免Map顺序问题。 - 对
FirebaseAuth.instance.currentUser添加空值判断,防止用户未登录时出现崩溃。
内容的提问来源于stack exchange,提问作者intel44
相关产品推荐
相关产品推荐

