Time Field serverTimestamp() in cloud Firestore returns null on the first snapshot
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.
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.
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:
- when sending the message, a write request is sent to the server, but the data isn't available yet.
- builder is trying to pull the data and gets null.
- data is available, and the NULL replaces with the real data. to solve that few milliseconds glitch, I've tried multiple solutions, but found this one the simplest: put an empty string until that data is available, the UI will be empty for about 0.5 seconds, but from my experience, it still looks good.
TL:DR
final sortTime = snapshot.data.documents[index].data()["sortTime"];
String serverTime = sortTime == null ? "" : sortTime.toDate().toString();
consider this Dart code as a brief example to explain with:
sending Msg function:
/// 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);
}
get the messages
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);
}
);
}
},
);
}
Panagiss
Undergraduate IT Student at the Department of Informatics and Telematics in Harokopio University of Athens(HUA)
Updated on December 24, 2022Comments
-
Panagiss over 1 year
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
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.
So here is the error:
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)
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.
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, }); }
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)Any ideas?
-
Panagiss over 3 yearsit solves it of course. But i don't get why serverTimestamp has this problem!
-
Panagiss over 3 yearsi will leave the post unanswered till i find out about serverTimestamp()
-
Mohammad I over 3 yearsThe following document may answer to your query: medium.com/firebase-developers/…
-
Michael B. about 2 yearsNot using flutter, but your answer brought me to some other docs for the JS lib and maybe this also works in flutter: the
doc.data()
function accepts aserverTimestamp
param, withestimate
/previous
the timestamp will not benull
in between updates that useserverTimestamp