Flutter Firestore聊天应用StateError:message字段不存在的解决
问题
开发基于Flutter的聊天应用,使用Firebase Firestore存储消息时,访问文档的message字段抛出以下错误:
Exception has occurred. StateError (Bad state: field "message" does not exist within the DocumentSnapshotPlatform)
相关代码(chat_page.dart)
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; class ChatPage extends StatefulWidget { static const String screenRoute = "ChatPage"; @override _ChatPageState createState() => _ChatPageState(); } class _ChatPageState extends State<ChatPage> { late User? signedInUser; TextEditingController _messageController = TextEditingController(); @override void initState() { super.initState(); getSignedInUser(); } void getSignedInUser() { final user = FirebaseAuth.instance.currentUser; setState(() { signedInUser = user; }); } Future<void> sendMessage(String message, String receiverEmail) async { try { if (signedInUser != null) { final messageData = { 'message': message, 'senderId': signedInUser!.uid, 'senderEmail': signedInUser!.email, 'receiverEmail': receiverEmail, 'timestamp': Timestamp.now(), }; await FirebaseFirestore.instance.collection('messages').add(messageData); _messageController.clear(); } } catch (e) { print('Error sending message: $e'); } } @override Widget build(BuildContext context) { final args = ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>; final name = args['name']; final imageLink = args['ImageLink']; final lastSeenTimestamp = args['lastSeen']; // Convert Firestore Timestamp to DateTime DateTime? lastSeenDateTime; if (lastSeenTimestamp != null && lastSeenTimestamp is Timestamp) { lastSeenDateTime = lastSeenTimestamp.toDate(); } return Scaffold( appBar: AppBar( leading: CircleAvatar( backgroundImage: NetworkImage(imageLink), ), title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(name), Text( lastSeenDateTime != null ? formatLastSeen(lastSeenDateTime) : 'Last seen unknown', style: TextStyle(fontSize: 12), ), ], ), ), body: Column( children: [ Expanded( child: StreamBuilder<QuerySnapshot>( stream: FirebaseFirestore.instance.collection('messages').snapshots(), builder: (context, snapshot) { if (!snapshot.hasData) { return Center( child: CircularProgressIndicator(backgroundColor: Colors.blue), ); } final messages = snapshot.data!.docs; return ListView.builder( itemCount: messages.length, itemBuilder: (context, index) { final message = messages[index]; final messageText = message['message']; // Error occurs here final senderId = message['senderId']; final isMe = signedInUser?.uid == senderId; return MessageReplay( sendmessage: messageText, isMe: isMe, ); }, ); }, ), ), Padding( padding: const EdgeInsets.all(8.0), child: Row( children: [ Expanded( child: TextField( controller: _messageController, decoration: InputDecoration( hintText: 'Type a message...', ), ), ), IconButton( icon: Icon(Icons.send), onPressed: () { final message = _messageController.text.trim(); final receiverEmail = 'receiver@example.com'; // Replace with the receiver's email address if (message.isNotEmpty) { sendMessage(message, receiverEmail); } }, ), ], ), ), ], ), ); } String formatLastSeen(DateTime lastSeen) { Duration difference = DateTime.now().difference(lastSeen); if (difference.inDays > 0) { return 'Last seen ${difference.inDays} days ago'; } else if (difference.inHours > 0) { return 'Last seen ${difference.inHours} hours ago'; } else if (difference.inMinutes > 0) { return 'Last seen ${difference.inMinutes} minutes ago'; } else { return 'Last seen just now'; } } } class MessageReplay extends StatelessWidget { final String? sendmessage; final bool isMe; const MessageReplay({Key? key, this.sendmessage, required this.isMe}) : super(key: key); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(10.0), child: Column( crossAxisAlignment: isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ Material( elevation: 5, borderRadius: BorderRadius.only( topLeft: Radius.circular(30), bottomLeft: isMe ? Radius.circular(30) : Radius.circular(0), bottomRight: isMe ? Radius.circular(0) : Radius.circular(30), topRight: Radius.circular(30), ), color: isMe ? Colors.blue : Colors.white, child: Padding( padding: const EdgeInsets.all(12.0), child: Text( sendmessage ?? 'Message not found', // Handle null message style: TextStyle(fontSize: 15, color: isMe ? Colors.white : Colors.black), ), ), ), ], ), ); } }
已尝试操作
- 确认发送消息时
message字段已正确添加到Firestore; - 验证Firestore集合和文档路径正确。
解决方案
1. 安全访问字段,避免直接索引取值
直接使用message['message']会在字段不存在时抛出异常,改用get()方法并设置默认值,或先检查字段是否存在:
修改StreamBuilder中的itemBuilder代码:
final message = messages[index]; // 方式1:使用get()方法并指定默认值 final messageText = message.get('message', defaultValue: ''); final senderId = message.get('senderId', defaultValue: ''); // 方式2:先检查字段存在性再取值 final messageData = message.data() as Map<String, dynamic>; final messageText = messageData.containsKey('message') ? messageData['message'] : ''; final senderId = messageData.containsKey('senderId') ? messageData['senderId'] : '';
2. 过滤无效文档,减少无效数据加载
在Firestore查询中添加过滤条件,只获取包含message和senderId字段的文档:
stream: FirebaseFirestore.instance .collection('messages') .where('message', isNotEqualTo: null) .where('senderId', isNotEqualTo: null) .snapshots(),
3. 清理或修复Firestore中的无效数据
如果Firestore中存在没有message字段的旧文档,直接在Firebase控制台删除这些无效文档,或批量更新补充缺失字段。
4. 引入强类型数据模型(可选,长期优化)
创建Message类封装数据,通过fromDocumentSnapshot方法安全解析文档,提升代码可维护性:
class Message { final String message; final String senderId; final String senderEmail; final String receiverEmail; final Timestamp timestamp; Message({ required this.message, required this.senderId, required this.senderEmail, required this.receiverEmail, required this.timestamp, }); factory Message.fromDocumentSnapshot(DocumentSnapshot doc) { final data = doc.data() as Map<String, dynamic>; return Message( message: data['message'] ?? '', senderId: data['senderId'] ?? '', senderEmail: data['senderEmail'] ?? '', receiverEmail: data['receiverEmail'] ?? '', timestamp: data['timestamp'] ?? Timestamp.now(), ); } }
使用时修改itemBuilder:
final message = Message.fromDocumentSnapshot(messages[index]); final messageText = message.message; final senderId = message.senderId;
内容的提问来源于stack exchange,提问作者Hussien990
相关产品推荐
相关产品推荐

