You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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集合数据截图

Firebase集合截图

问题原因与解决方案

问题根源

当前Stream的查询条件仅筛选了当前用户作为发送方,对方作为接收方的消息:

stream: FirebaseFirestore.instance
    .collection('chats')
    .where('senderuid',isEqualTo: FirebaseAuth.instance.currentUser!.uid )
    .where('recieveruid',isEqualTo: widget.recieveruid)
    .snapshots(),

而接收的消息是对方作为发送方,当前用户作为接收方,这类数据完全不在查询结果里,因此无法展示。

修复方案

修改Stream查询,同时包含两种对话场景:

  1. 当前用户是发送方,对方是接收方
  2. 当前用户是接收方,对方是发送方

同时添加按时间排序,保证消息顺序正确:

方案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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 06:10:32