云 Firestore 中的时间字段 serverTimestamp() 在第一个快照上返回 null [英] Time Field serverTimestamp() in cloud Firestore returns null on the first snapshot

查看:24
本文介绍了云 Firestore 中的时间字段 serverTimestamp() 在第一个快照上返回 null的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在从 Cloud Firestore 读取一些数据(消息),并且在文档中我有一个 timestamp 字段来表示时间.我有一个流:

Im reading some data(messages) from Cloud Firestore and in a document i have a timestamp field for time. I have a Stream:

Stream<QuerySnapshot> get chats {
    return chatCollection.document(roomid).collection("messages").snapshots();
  }

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

for getting the messages if a an update occurs in my database (ex New message).

所以当我启动应用程序时,它会从数据库中读取所有数据(消息)并将它们打印到我的应用程序中.这是我每次获得新快照时控制台打印消息的内容:

So when i start the app it reads all the data(messages) from the DB and i print them in my app. Here is what my console printing the messages every time i get a new snapshot:

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
D/ViewRootImpl@a0e5145[MainActivity]( 3045): Relayout returned: old=[0,0][1440,2560] new=[0,0][1440,2560] result=0x1 surface={valid=true 492514541568} changed=false
I/flutter ( 3045): DEBUG: TEST-MESSAGE what??? 2020-10-09 22:30:12.249
I/flutter ( 3045): DEBUG: TEST-MESSAGE λολ 2020-10-09 21:59:58.212
I/flutter ( 3045): DEBUG: TEST-MESSAGE gamieste 2020-10-09 22:33:10.902
I/flutter ( 3045): DEBUG: TEST-MESSAGE holly 2020-10-09 22:26:39.672
I/flutter ( 3045): DEBUG: TEST-MESSAGE γφ 2020-10-09 22:08:47.617
I/flutter ( 3045): DEBUG: TEST-MESSAGE ελα 2020-10-09 22:13:38.167
I/flutter ( 3045): DEBUG: TEST-MESSAGE see re 2020-10-09 22:29:14.277
I/flutter ( 3045): DEBUG: TEST-MESSAGE ιξι 2020-10-09 22:10:07.442
I/flutter ( 3045): DEBUG: TEST-MESSAGE ολα καλα ρε 2020-10-09 22:05:00.703
I/flutter ( 3045): DEBUG: TEST-MESSAGE what??? 2020-10-09 22:30:12.249
I/flutter ( 3045): DEBUG: TEST-MESSAGE λολ 2020-10-09 21:59:58.212
I/flutter ( 3045): DEBUG: TEST-MESSAGE gamieste 2020-10-09 22:33:10.902
I/flutter ( 3045): DEBUG: TEST-MESSAGE holly fuck 2020-10-09 22:26:39.672
I/flutter ( 3045): DEBUG: TEST-MESSAGE γφ 2020-10-09 22:08:47.617
I/flutter ( 3045): DEBUG: TEST-MESSAGE ελα μου 2020-10-09 22:13:38.167

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

So no problem on that. The problem occurs when i add a new document(message) in my DB from my code, then immediately it gets the update from the DB and i have the new snapshot with the new list of messages. But for about 0.5 sec i get an error on the screen oh my android and then it gets normal and has loaded all messages correctly. The error is a null pointer for a specific field of the document. TIME. Time is timestamp field in my Cloud Firestore DB.

所以这是错误:

The following NoSuchMethodError was thrown building:
The method 'toDate' was called on null.
Receiver: null
Tried calling: toDate()

When the exception was thrown, this was the stack:
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1      _ChatScreenState._buildMessage (package:flutter_firebase_chat_app/screens/chat/chat_screen.dart:48:43)
#2      _ChatScreenState.build.<anonymous closure>.<anonymous closure> (package:flutter_firebase_chat_app/screens/chat/chat_screen.dart:226:40)
#3      SliverChildBuilderDelegate.build (package:flutter/src/widgets/sliver.dart:448:22)
#4      SliverMultiBoxAdaptorElement._build.<anonymous closure> (package:flutter/src/widgets/sliver.dart:1136:67)
#5      _HashMap.putIfAbsent (dart:collection-patch/collection_patch.dart:140:29)
#6      SliverMultiBoxAdaptorElement._build (package:flutter/src/widgets/sliver.dart:1136:26)
#7      SliverMultiBoxAdaptorElement.performRebuild.processElement (package:flutter/src/widgets/sliver.dart:1082:66) 

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

You will see that its just a null exception. The thing is why im getting a null for a Time field only when i upload a message. And the thing is that after that the message comes again from another snapshot with no-null value!! If you look on the error code segment i have printed the whole object and in the top right corner you will see that time field has null value. I made an if statement to print the whole object whenever time field is null.

所以最后我认为造成这种情况的原因是时间字段是一个时间戳,当我上传数据时,我给它一个 ServerTimestamp() 的值.代码在这里:

So at last the thing i think is causing this is that time field is a timestamp and when i upload the data i give it a value of ServerTimestamp(). Code here:

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

如您所见,Time 字段是唯一使用该 FieldValue.serverTimestamp() 的字段,所以我认为在时间戳值到字段时间之前,我以某种方式获取了文档的快照分配的.在我得到它之后(时间字段不为空)

As you can see the Time field is the only one that takes that FieldValue.serverTimestamp() , so i think that somehow i get snapshot of the document before the Timestamp value to field time has been assigned. And after i get it all (with the Time field being no-Null)

有什么想法吗?

推荐答案

我也遇到了同样的问题.经过广泛搜索,我发现了这个 文章非常有用.它有助于理解问题和导致问题的流程.

I've also encountered the same problem. and after a wide search, I've found this article very useful. It helps to understand the issue and the flow causes the problem.

使用 DateTime.now()不是解决方案,因为使用 FieldValue.serverTimestamp() 的主要目的是在不同设备之间同步时间,DateTime.now()没有解决.

Using DateTime.now() is not a solution since the main purpose of using the FieldValue.serverTimestamp() is to sync times between different devices, which DateTime.now() does not solves.

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

now, regarding the NULL issue with the FieldValue: I've also used sortTime.toDate().toString() as suggested Here , but getting NULL on the timeStamp, because:

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

TL:DR

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

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

consider this Dart code as a brief example to explain with:

发送消息功能:

/// 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);
}

获取消息

getConversationMessages(String chatRoomId) async {
  return await chatsCollection
      .doc(chatRoomId)
      .collection("chats")
      .orderBy("sortTime", descending: true)
      .snapshots();
}

Stream chatMessagesStream;
void initState(){
    getConversationMessages(widget.chatRoomId)
      .then((value){
        setState(() {
          chatMessagesStream = value;
        });
    });
    super.initState();
  }

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);
                }
            );
          }
        },
    );
  }

这篇关于云 Firestore 中的时间字段 serverTimestamp() 在第一个快照上返回 null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆