使用Firebase Firestore无法展示接收消息的技术求助
Firebase Firestore 聊天消息接收展示问题
开发聊天功能时遇到问题:发送的消息能正常显示,但接收的消息无法加载展示。当前通过Stream Builder获取Firestore的chats集合数据,但仅能渲染自己发送的消息。
相关代码
消息页面(含Stream Builder)
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:instagram_clone/Models/user_model.dart'; import 'package:instagram_clone/Provider/user_provider.dart'; import 'package:instagram_clone/utils/circular_progress_indicator.dart'; import 'package:instagram_clone/utils/colors.dart'; import 'package:instagram_clone/utils/utils.dart'; import 'package:instagram_clone/widgets/message_bubbles.dart'; import 'package:provider/provider.dart'; import '../resources/Firestore_methods.dart'; class MessagesScreen extends StatefulWidget { String recieveruid; MessagesScreen({Key? key,required this.recieveruid}) : super(key: key); @override State<MessagesScreen> createState() => _MessagesScreenState(); } class _MessagesScreenState extends State<MessagesScreen> { String recieverName=' '; String reciverDp=' '; bool isFetched=false; TextEditingController messagecontoller=TextEditingController(); void sendMessage()async{ if(messagecontoller.text.isNotEmpty){ String res=await FireStoreMethods().sendMessage( senderUid: FirebaseAuth.instance.currentUser!.uid, recieverUid: widget.recieveruid, message: messagecontoller.text); if(res!='success'){ Utils().showSnackBar(context, 'Unable to send message kindly check your connection', Colors.red); } } } void getRecieverDetails()async{ DocumentSnapshot ref= await FirebaseFirestore.instance.collection('users').doc(widget.recieveruid).get(); recieverName=ref['username']; reciverDp=ref['photourl']; setState(() { isFetched=true; }); } @override void initState() { print(widget.recieveruid); getRecieverDetails(); // TODO: implement initState super.initState(); } @override void dispose() { messagecontoller.dispose(); // TODO: implement dispose super.dispose(); } bool _isReciever=true; @override Widget build(BuildContext context) { UserModel user=Provider.of<UserProvider>(context).getUser; return !isFetched?Center(child: CircularIndicator,):Scaffold( appBar: AppBar( title: Text(recieverName), backgroundColor: mobileBackgroundColor, centerTitle: true, ), body: Stack( children: [ Container( height: MediaQuery.of(context).size.height*0.8, child: StreamBuilder( stream: FirebaseFirestore.instance .collection('chats') .where('senderuid',isEqualTo: FirebaseAuth.instance.currentUser!.uid ) .where('recieveruid',isEqualTo: widget.recieveruid) .snapshots(), builder: (context,AsyncSnapshot<QuerySnapshot<Map<String,dynamic>>>snapshot){ if(!snapshot.hasData || snapshot.connectionState==ConnectionState.waiting){ return Center(child: CircularIndicator); } else{ return ListView.builder( itemCount: snapshot.data!.docs.length, itemBuilder: (context,index){ if(snapshot.data!.docs[index]['senderuid']==FirebaseAuth.instance.currentUser!.uid){ return MessageBubbles(profilePic: user.photourl, message: snapshot.data!.docs[index]['message'], date: snapshot.data!.docs[index]['datetime'], isReciever: false); } else{ return MessageBubbles( profilePic: reciverDp, message: snapshot.data!.docs[index]['message'], date:snapshot.data!.docs[index]['datetime'], isReciever: true); } }); } } ), ), Align( alignment: Alignment.bottomCenter,child: Container( child: Row( children: [ Expanded( child: TextFormField( controller: messagecontoller, maxLines: null, decoration: InputDecoration( hintText: 'Message...', contentPadding: EdgeInsets.symmetric(horizontal: 8), border: InputBorder.none ), ), ), TextButton(onPressed: ()async{ sendMessage(); messagecontoller.clear(); }, child: Text('Send',style: TextStyle( color: Colors.blueAccent, fontWeight: FontWeight.bold, fontSize: 15 ),)) ], ), decoration: BoxDecoration( border: Border.all(color: secondaryColor), borderRadius: BorderRadius.circular(20), color: mobileBackgroundColor, ), ),), ], ), ); } }
消息气泡组件
import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../utils/colors.dart'; class MessageBubbles extends StatefulWidget { String profilePic; String message; final date; bool isReciever; MessageBubbles({Key? key, required this.profilePic, required this.message, required this.date, required this.isReciever }) : super(key: key); @override State<MessageBubbles> createState() => _MessageBubblesState(); } class _MessageBubblesState extends State<MessageBubbles> { @override Widget build(BuildContext context) { return widget.isReciever?Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ CircleAvatar( backgroundImage: NetworkImage(widget.profilePic), ), FittedBox( child: Container( width: MediaQuery.of(context).size.width*0.6, constraints: BoxConstraints( maxWidth: double.infinity, maxHeight: double.infinity ), decoration: BoxDecoration( borderRadius: BorderRadius.only (topRight: Radius.circular(10), bottomLeft: Radius.circular(10), bottomRight: Radius.circular(10), ), color: Colors.grey.withOpacity(0.3), ), child: Padding( padding: const EdgeInsets.all(10.0), child: Text(widget.message), ), ), ), Text(DateFormat.yMMMd().format(widget.date.toDate()),style: TextStyle( color: secondaryColor, fontSize: 12 ),), ], ), ], ), ): Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), child: Column( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.end, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Text(DateFormat.yMMMd().format(widget.date.toDate()).toString(),style: TextStyle( color: secondaryColor, fontSize: 12 ),), FittedBox( child: Container( width: MediaQuery.of(context).size.width*0.6, constraints: BoxConstraints( maxWidth: double.infinity, maxHeight: double.infinity ), decoration: BoxDecoration( borderRadius: BorderRadius.only (topRight: Radius.circular(10), bottomLeft: Radius.circular(10), topLeft: Radius.circular(10), ), color: Colors.blueAccent, ), child: Padding( padding: const EdgeInsets.all(10.0), child: Text(widget.message), ), ), ), CircleAvatar( backgroundImage: NetworkImage(widget.profilePic), ), ], ), ], ), ); } }
界面与Firestore数据截图
界面输出截图

Firebase集合数据截图

问题原因与解决方案
问题根源
当前Stream的查询条件仅筛选了当前用户作为发送方,对方作为接收方的消息:
stream: FirebaseFirestore.instance .collection('chats') .where('senderuid',isEqualTo: FirebaseAuth.instance.currentUser!.uid ) .where('recieveruid',isEqualTo: widget.recieveruid) .snapshots(),
而接收的消息是对方作为发送方,当前用户作为接收方,这类数据完全不在查询结果里,因此无法展示。
修复方案
修改Stream查询,同时包含两种对话场景:
- 当前用户是发送方,对方是接收方
- 当前用户是接收方,对方是发送方
同时添加按时间排序,保证消息顺序正确:
方案1(使用Firebase Filter API,适用于新版本)
stream: FirebaseFirestore.instance .collection('chats') .where(Filter.or( Filter('senderuid', isEqualTo: FirebaseAuth.instance.currentUser!.uid), Filter('senderuid', isEqualTo: widget.recieveruid) )) .where(Filter.or( Filter('recieveruid', isEqualTo: FirebaseAuth.instance.currentUser!.uid), Filter('recieveruid', isEqualTo: widget.recieveruid) )) .orderBy('datetime', descending: false) .snapshots(),
方案2(使用whereIn,兼容旧版本)
stream: FirebaseFirestore.instance .collection('chats') .where('senderuid', whereIn: [FirebaseAuth.instance.currentUser!.uid, widget.recieveruid]) .where('recieveruid', whereIn: [FirebaseAuth.instance.currentUser!.uid, widget.recieveruid]) .orderBy('datetime', descending: false) .snapshots(),
注意事项
修改后运行代码时,Firestore会提示需要创建复合索引,按照控制台给出的链接跳转并创建即可,否则查询会报错。
内容的提问来源于stack exchange,提问作者Hamza Khan
相关产品推荐
相关产品推荐

