云 Firestore 中的时间字段 serverTimestamp() 在第一个快照上返回 null

Pan*_*iss 8 timestamp firebase flutter google-cloud-firestore

我从 Cloud Firestore 读取一些数据(消息),在文档中我有一个timestamp时间字段。\n我有一个流:

\n
Stream<QuerySnapshot> get chats {\n    return chatCollection.document(roomid).collection("messages").snapshots();\n  }\n
Run Code Online (Sandbox Code Playgroud)\n

用于在我的数据库中发生更新(例如新消息)时获取消息。

\n

因此,当我启动应用程序时,它会从数据库读取所有数据(消息)并打印出来。\n这是每次获取新快照时我的控制台和消息的样子:

\n
D/ViewRootImpl@a0e5145[MainActivity]( 3045): MSG_RESIZED: frame=Rect(0, 0 - 1440, 2560) ci=Rect(0, 96 - 0, 1164) vi=Rect(0, 96 - 0, 1164) or=1\nD/ViewRootImpl@a0e5145[MainActivity]( 3045): Relayout returned: old=[0,0][1440,2560] new=[0,0][1440,2560] result=0x1 surface={valid=true 492514541568} changed=false\nI/flutter ( 3045): DEBUG: TEST-MESSAGE what??? 2020-10-09 22:30:12.249\nI/flutter ( 3045): DEBUG: TEST-MESSAGE \xce\xbb\xce\xbf\xce\xbb 2020-10-09 21:59:58.212\nI/flutter ( 3045): DEBUG: TEST-MESSAGE gamieste 2020-10-09 22:33:10.902\nI/flutter ( 3045): DEBUG: TEST-MESSAGE holly 2020-10-09 22:26:39.672\nI/flutter ( 3045): DEBUG: TEST-MESSAGE \xce\xb3\xcf\x86 2020-10-09 22:08:47.617\nI/flutter ( 3045): DEBUG: TEST-MESSAGE \xce\xb5\xce\xbb\xce\xb1 2020-10-09 22:13:38.167\nI/flutter ( 3045): DEBUG: TEST-MESSAGE see re 2020-10-09 22:29:14.277\nI/flutter ( 3045): DEBUG: TEST-MESSAGE \xce\xb9\xce\xbe\xce\xb9 2020-10-09 22:10:07.442\nI/flutter ( 3045): DEBUG: TEST-MESSAGE \xce\xbf\xce\xbb\xce\xb1 \xce\xba\xce\xb1\xce\xbb\xce\xb1 \xcf\x81\xce\xb5 2020-10-09 22:05:00.703\nI/flutter ( 3045): DEBUG: TEST-MESSAGE what??? 2020-10-09 22:30:12.249\nI/flutter ( 3045): DEBUG: TEST-MESSAGE \xce\xbb\xce\xbf\xce\xbb 2020-10-09 21:59:58.212\nI/flutter ( 3045): DEBUG: TEST-MESSAGE gamieste 2020-10-09 22:33:10.902\nI/flutter ( 3045): DEBUG: TEST-MESSAGE holly fuck 2020-10-09 22:26:39.672\nI/flutter ( 3045): DEBUG: TEST-MESSAGE \xce\xb3\xcf\x86 2020-10-09 22:08:47.617\nI/flutter ( 3045): DEBUG: TEST-MESSAGE \xce\xb5\xce\xbb\xce\xb1 \xce\xbc\xce\xbf\xcf\x85 2020-10-09 22:13:38.167\n
Run Code Online (Sandbox Code Playgroud)\n

所以这没有问题。当我从代码中在数据库中添加新文档(消息),然后立即从数据库获取更新并且我拥有包含新消息列表的新快照时,就会出现问题。但大约 0.5 秒后,我的 Android 屏幕上出现错误,然后它变得正常并正确加载了所有消息。该错误是文档特定字段的空指针。时间。时间是我的 Cloud Firestore DB 中的时间戳字段。

\n

所以这是错误:

\n
The following NoSuchMethodError was thrown building:\nThe method 'toDate' was called on null.\nReceiver: null\nTried calling: toDate()\n\nWhen the exception was thrown, this was the stack:\n#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)\n#1      _ChatScreenState._buildMessage (package:flutter_firebase_chat_app/screens/chat/chat_screen.dart:48:43)\n#2      _ChatScreenState.build.<anonymous closure>.<anonymous closure> (package:flutter_firebase_chat_app/screens/chat/chat_screen.dart:226:40)\n#3      SliverChildBuilderDelegate.build (package:flutter/src/widgets/sliver.dart:448:22)\n#4      SliverMultiBoxAdaptorElement._build.<anonymous closure> (package:flutter/src/widgets/sliver.dart:1136:67)\n#5      _HashMap.putIfAbsent (dart:collection-patch/collection_patch.dart:140:29)\n#6      SliverMultiBoxAdaptorElement._build (package:flutter/src/widgets/sliver.dart:1136:26)\n#7      SliverMultiBoxAdaptorElement.performRebuild.processElement (package:flutter/src/widgets/sliver.dart:1082:66) \n
Run Code Online (Sandbox Code Playgroud)\n

您将看到它只是一个空异常。问题是为什么只有当我上传消息时,时间字段才会为空。问题是,此后消息再次来自另一个具有非空值的快照!如果您查看错误代码段,我已经打印了整个对象,并且在右上角您将看到时间字段具有空值。我做了一个 if 语句,每当时间字段为空时就打印整个对象。

\n

所以最后我认为导致这个问题的是时间字段是一个时间戳,当我上传数据时我给它一个值ServerTimestamp()。\n这里的代码:

\n
Future sendMessage(Message message) async {\n    if (message.senderId != AuthService.myUid) return null;\n    return await chatCollection.document(roomid).collection("messages").add({\n      "message": message.text,\n      "sender": message.senderId,\n      "receiver": message.receiverId,\n      "time": FieldValue.serverTimestamp(),\n      "isLiked": message.isLiked,\n      "unread": message.unread,\n    });\n  }\n
Run Code Online (Sandbox Code Playgroud)\n

正如您所看到的,“时间”字段是唯一采用该字段的字段FieldValue.serverTimestamp(),因此我认为在将时间戳值分配给字段时间之前,我会以某种方式获取文档的快照。当我得到这一切之后(时间字段不为空)

\n

有任何想法吗?

\n

Roe*_*zur 4

我也遇到了同样的问题。经过广泛的搜索,我发现这篇文章非常有用。它有助于理解问题以及流程导致问题的原因。

使用DateTime.now() 不是解决方案,因为使用的主要目的FieldValue.serverTimestamp() 是在不同设备之间同步时间,但这并DateTime.now()不能解决问题。

现在,关于 FieldValue 的 NULL 问题:我也sortTime.toDate().toString()按照建议使用了Here,但在时间戳上得到 NULL,因为:

  1. 发送消息时,会向服务器发送写入请求,但数据尚不可用。
  2. 构建器正在尝试提取数据并获取空值。
  3. 数据可用,NULL 替换为真实数据。为了解决这几毫秒的故障,我尝试了多种解决方案,但发现这个最简单:放置一个空字符串,直到数据可用,UI 将空大约 0.5 秒,但从我的经验来看,它看起来仍然不错。

翻译:博士

final sortTime = snapshot.data.documents[index].data()["sortTime"];
String serverTime = sortTime == null ? "" : sortTime.toDate().toString();
Run Code Online (Sandbox Code Playgroud)

将此 Dart 代码视为一个简短的示例来解释:

发送消息功能:

/// on pressed action (send to firebase, and show message)
/// consider the messageController gets the input from the user
sendMessage(MyUser myUser) async {
    if (messageController.text.isNotEmpty){
      Map<String, dynamic> chatMessage = {
        "senderUid" : myUser.userId,
        "text" : messageController.text,
        "sortTime" : FieldValue.serverTimestamp(),
      };
      messageController.text = "";
      await addConversationMessages(widget.chatRoomId, chatMessage);
    }
}

Future addConversationMessages(String chatRoomId, chatMessage) async {
  await chatsCollection
      .doc(chatRoomId)
      .collection("chats")
      .add(chatMessage);
}

Run Code Online (Sandbox Code Playgroud)

获取消息

getConversationMessages(String chatRoomId) async {
  return await chatsCollection
      .doc(chatRoomId)
      .collection("chats")
      .orderBy("sortTime", descending: true)
      .snapshots();
}
Run Code Online (Sandbox Code Playgroud)
Stream chatMessagesStream;
void initState(){
    getConversationMessages(widget.chatRoomId)
      .then((value){
        setState(() {
          chatMessagesStream = value;
        });
    });
    super.initState();
  }
Run Code Online (Sandbox Code Playgroud)
Widget ListViewMessages(MyUser myUser){
    return StreamBuilder(
        stream: chatMessagesStream,
        builder: (context, snapshot){
          if(!snapshot.hasData){
            return Center(
                child: CircularProgressIndicator(
                  valueColor: AlwaysStoppedAnimation<Color>(Color(0xFF087E8B)),
                )
            );
          } else {
            return ListView.builder(
                reverse: true,
                itemCount: snapshot.data.documents.length,
                itemBuilder: (context, index) {
                  final msgText = snapshot.data.documents[index].data()["text"];
                  final msgUid = snapshot.data.documents[index]
                      .data()["senderUid"];
                  final sortTime = snapshot.data.documents[index].data()["sortTime"];
                  String serverTime = sortTime == null ? "" : sortTime.toDate().toString();
                  return _buildMessage(
                      msgText, serverTime, msgUid);
                }
            );
          }
        },
    );
  }
Run Code Online (Sandbox Code Playgroud)