Flutter聊天应用程序中的消息顺序不正确。

7
我刚刚完成了一个基于教程的基本聊天应用程序。新消息应该显示在底部,但这并没有发生。 当我删除、保存并再次添加行中的“反转”时: final messages = snapshot.data.documents.reversed; 然后它似乎重新排序了,但是下一条消息又变成随机的了。 唯一的区别是我正在使用更新的firebase插件。 firebase_core: 0.4.0+8 教程是 ^0.3.4 firebase_auth: 0.14.0+1 教程是 ^0.8.4+4 cloud_firestore: 0.12.9 教程是 ^0.9.13+1
MessageStream 代码:
class MessagesStream extends StatelessWidget {

@override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('messages').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
 backgroundColor: Colors.lightBlue,
    ),
  );
}
final messages = snapshot.data.documents.reversed;
List<MessageBubble> messageBubbles = [];
for (var message in messages) {
  final messageText = message.data['text'];
  final messageSender = message.data['sender'];

  final currentUser = loggedInUser.email;

  final messageBubble = MessageBubble(
    sender: messageSender,
    text: messageText,
    isMe: currentUser == messageSender,
  );

  messageBubbles.add(messageBubble);
}
return Expanded(
  child: ListView(
    reverse: true,
    padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 10.0),
    children: messageBubbles,
    ),
   );
  },
);
}
}

除了无序的问题,一切都正常工作,消息已保存在 Firebase 中。也许我需要添加时间码或其他内容来使它正常工作。
欢迎提供任何帮助-谢谢。
6个回答

11
应用这段代码后,就像Angela在她的课程视频中所做的那样,删除firebase中的数据库。然后重新启动应用程序。您的应用程序将正常工作。此外,在代码中,我已经添加了关于您需要添加的内容的说明。只需添加它们即可。 我遇到了同样的问题,所以我请一位编码人员帮助解决了这个问题。因此,它也将在您的代码中起作用。
import 'package:flutter/material.dart';
import 'package:flash_chat/constants.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

final _fireStore = Firestore.instance;
FirebaseUser loggedInUser;

class ChatScreen extends StatefulWidget {
  static String chatScreen = 'ChatScreenpage1';
  @override
  _ChatScreenState createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen> {
  final messageTextEditingController = TextEditingController();
  String messageText;

  final _auth = FirebaseAuth.instance;

  @override
  void initState() {
    super.initState();
    getUserDetail();
  }

  void getUserDetail() async {
    try {
      final createdUser = await _auth.currentUser();
      if (createdUser != null) {
        loggedInUser = createdUser;
      }
    } catch (e) {
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: null,
        actions: <Widget>[
          IconButton(
              icon: Icon(Icons.close),
              onPressed: () {

                _auth.signOut();
                Navigator.pop(context);
              }),
        ],
        title: Text('⚡️Chat'),
        backgroundColor: Colors.lightBlueAccent,
      ),
      body: SafeArea(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            StreambuilderClass(),
            Container(
              decoration: kMessageContainerDecoration,
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  Expanded(
                    child: TextField(
                      controller: messageTextEditingController,
                      onChanged: (value) {
                        messageText = value;
                      },
                      decoration: kMessageTextFieldDecoration,
                    ),
                  ),
                  FlatButton(
                    onPressed: () {
                      messageTextEditingController.clear();
                      _fireStore.collection('messages').add({
                        'sender': loggedInUser.email,
                        'text': messageText,
                        'time': FieldValue.serverTimestamp() //add this
                      });
                    },
                    child: Text(
                      'Send',
                      style: kSendButtonTextStyle,
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class StreambuilderClass extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
        stream: _fireStore
            .collection('messages')
            .orderBy('time', descending: false)//add this
            .snapshots(),
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return Center(
              child: CircularProgressIndicator(
                backgroundColor: Colors.blueAccent,
              ),
            );
          }
          final messages = snapshot.data.documents.reversed;
          List<MessageBubble> messageBubbles = [];
          for (var message in messages) {
            final messageText = message.data['text'];
            final messageSender = message.data['sender'];
            final messageTime = message.data['time'] as Timestamp; //add this
            final currentUser = loggedInUser.email;

            final messageBubble = MessageBubble(
              sender: messageSender,
              text: messageText,
              isMe: currentUser == messageSender,
              time: messageTime, //add this
            );

            messageBubbles.add(messageBubble);
          }

          return Expanded(
            child: ListView(
                reverse: true,
                padding: EdgeInsets.symmetric(horizontal: 10, vertical: 20),
                children: messageBubbles),
          );
        });
  }
}

class MessageBubble extends StatelessWidget {
  final String text;
  final String sender;
  final bool isMe;
  final Timestamp time; // add this

  MessageBubble({this.text, this.sender, this.isMe, this.time}); //add the variable  in this constructor
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(10.0),
      child: Column(
        crossAxisAlignment:
            isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
        children: <Widget>[
          Text(
            ' $sender ${DateTime.fromMillisecondsSinceEpoch(time.seconds * 1000)}',// add this only if you want to show the time along with the email. If you dont want this then don't add this DateTime thing
            style: TextStyle(color: Colors.black54, fontSize: 12),
          ),
          Material(
            color: isMe ? Colors.blueAccent : Colors.white,
            borderRadius: isMe
                ? BorderRadius.only(
                    topLeft: Radius.circular(30),
                    bottomLeft: Radius.circular(30),
                    bottomRight: Radius.circular(30))
                : BorderRadius.only(
                    topRight: Radius.circular(30),
                    bottomLeft: Radius.circular(30),
                    bottomRight: Radius.circular(30)),
            elevation: 6,
            child: Padding(
              padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15),
              child: Text(
                text,
                style: TextStyle(
                    fontSize: 20, color: isMe ? Colors.white : Colors.black),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

2
嗨,也许你可以更好地解释一下问题,而不是再次粘贴整个代码。此外,没有人关心安吉拉是谁 :-)(我假设要点是,firestore不能保证保持创建顺序,因此您必须添加一个属性(时间),以便进行排序?) - Herbert Poul
1
我理解您的担忧。但是,我发现完整的代码将是理解整个问题的更好方式。由于这段代码是Angela课程的一部分,所以我相信那些参加过她课程的人会更容易理解。我只是在这里添加了时间戳,并通过在数据库中创建一个时间字段来按时间排序所有消息,同时包括发送者和文本字段。 - GAURAV JOSHI
你好,这段代码是否在任何用户同时发送新消息时也会更新聊天屏幕?我正在寻找一种代码,在打开聊天屏幕时不仅调用Firestore查询以获取消息,而且如果有任何用户在线并发送文本,则应该只显示他们在那里发送的消息。这是可能的吗? - user14713384
我们正在使用Stream Builder,它会自动从Firestore获取消息,每当用户发送一条消息时。它完成了基本聊天应用程序的预期工作,即用户输入消息并立即显示给其他用户。 - GAURAV JOSHI

6

这是完整可运行的解决方案。感谢jquevedo和appbrewery。

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flash_chat/constants.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

final _firestore = Firestore.instance;
FirebaseUser loggedInUser;

class ChatScreen extends StatefulWidget {
  static const String id = "chat_screen";

  @override
  _ChatScreenState createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen> {
  final messageTextContoller = TextEditingController();
  final _auth = FirebaseAuth.instance;

  String messageText;

  @override
  void initState() {
    super.initState();
    getCurrentUser();
  }

  void getCurrentUser() async {
    try {
      final user = await _auth.currentUser();
      if (user != null) {
        loggedInUser = user;
        print(loggedInUser.email);
      }
    } catch (e) {
      print(e);
    }
  }

  void messagesStream() async {
    await for (var snapshot in _firestore.collection("messages").snapshots()) {
      for (var message in snapshot.documents) {
        print(message.data);
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: null,
        actions: <Widget>[
          IconButton(
              icon: Icon(Icons.close),
              onPressed: () {
                _auth.signOut();
                Navigator.pop(context);
              }),
        ],
        title: Text('⚡️Chat'),
        backgroundColor: Colors.lightBlueAccent,
      ),
      body: SafeArea(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            MessagesStream(),
            Container(
              decoration: kMessageContainerDecoration,
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  Expanded(
                    child: TextField(
                      controller: messageTextContoller,
                      onChanged: (value) {
                        messageText = value;
                      },
                      decoration: kMessageTextFieldDecoration,
                    ),
                  ),
                  FlatButton(
                    onPressed: () {
                      messageTextContoller.clear();
                      _firestore.collection("messages").add({
                        "text": messageText,
                        "sender": loggedInUser.email,
                        "time": DateTime.now()
                      });
                    },
                    child: Text(
                      'Send',
                      style: kSendButtonTextStyle,
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class MessagesStream extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
      stream: _firestore.collection("messages").snapshots(),
      // ignore: missing_return
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return Center(
            child: CircularProgressIndicator(
              backgroundColor: Colors.lightBlueAccent,
            ),
          );
        }
        final messages = snapshot.data.documents;
        List<MessageBubble> messageBubbles = [];
        for (var message in messages) {
          final messageText = message.data['text'];
          final messageSender = message.data["sender"];
          final messageTime = message.data["time"];
          final currentUsser = loggedInUser.email;
          final messageBubble = MessageBubble(
            sender: messageSender,
            text: messageText,
            time: messageTime,
            isMe:  currentUsser == messageSender,
          );
          messageBubbles.add(messageBubble);
          messageBubbles.sort((a , b ) => b.time.compareTo(a.time));
        }
        return Expanded(
          child: ListView(
            reverse: true,
            padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20),
            children: messageBubbles,
          ),
        );
      },
    );
  }
}

class MessageBubble extends StatelessWidget {
  final String sender;
  final String text;
  final Timestamp time;
  final bool isMe;

  MessageBubble({this.sender, this.text, this.isMe, this.time});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(10.0),
      child: Column(
        crossAxisAlignment: isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
        children: <Widget>[
          Text(
            "$sender ${time.toDate()}",
            style: TextStyle(
              color: Colors.grey,
              fontSize: 12,
            ),
          ),
          Material(
            borderRadius: isMe ? BorderRadius.only(
              topLeft: Radius.circular(30),
              bottomLeft: Radius.circular(30),
              bottomRight: Radius.circular(30),
            ) : BorderRadius.only(
              topRight: Radius.circular(30),
              bottomLeft: Radius.circular(30),
              bottomRight: Radius.circular(30),
            ),
            elevation: 5.0,
            color: isMe ? Colors.lightBlueAccent : Colors.purpleAccent,
            child: Padding(
              padding: EdgeInsets.symmetric(
                vertical: 10,
                horizontal: 20,
              ),
              child: Text(
                text,
                style: TextStyle(
                  fontSize: 15,
                  color: Colors.white,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}


1
请在你的代码中添加另外两条语句以获得有序结果。对于相同的代码,我已经放置了点号。
  return StreamBuilder<QuerySnapshot>(
  stream: _firestore.collection('messages').orderBy('date').snapshots(),
  builder: (context, snapshot) {
    if (!snapshot.hasData) {
      return Center(
        child: CircularProgressIndicator(
          backgroundColor: Colors.lightBlueAccent,
        ),
      );
    }
    ....
FlatButton(
                   onPressed: () {
                   messageTextController.clear();
                  _firestore.collection("messages").add({
                    'sender': loggedInUser.email,
                    'text': messageText,
                    'date': DateTime.now().toIso8601String().toString(),
                  });
                  },
           ...

1
var notSortedMEssages = snapshot.data.documents;
notSortedMEssages.sort((a,b) => a['time'].compareTo(b['time']);
var reversedSortedMEssages = List.from(notSortedMEssages.reversed);
List<MessageBubble> messageBubbles = [];

如果您能够解释这个答案是如何回答这个问题的话,那会很有帮助。 - Dragonthoughts

0

只需跟着这个做

 FirebaseFirestore firestore = FirebaseFirestore.instance;

 final Stream<QuerySnapshot> _usersStream = FirebaseFirestore.instance
        .collection("Message")
        .orderBy("time", descending: true)
        .snapshots();

  firestore.collection('Message').add({
                        "Email": _auth.currentUser!.email,
                        "message": message,
                        "time": DateTime.now(),
                      });

0

显示类型“String”不是类型“Timestamp”的子类型


我不明白它与作者的问题有什么关系。你能详细解释一下吗? - CircleOnCircles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接