firebase - Read single document field from Firestore (Flutter)
问题描述
I have a document saved on Firestore, this document has a field "body" with some text inside of type "String".
It is within a collection called "messages", its path is : "chats/chatId/messages".
So my goal is to read this collection "messages" then get the latest element in it and read its field "body" printing that value on terminal.
My problem is that I read nothing.
This is the code :
Future<String> readMessageFromChat(String chatId, int limit) async {
Stream<QuerySnapshot> collectionToRead = await FirebaseFirestore.instance
.collection("chats")
.doc(chatId)
.collection("messages")
.limit(limit)
.snapshots();
String ret = "I have read the message : ";
collectionToRead.forEach((element) {
ret += element.docs.last.data()["body"].toString();
});
return ret;
}
And this is the output :
flutter: id (changes randomly)
flutter: I have read the message :
No message is read, as you can see.
I will write also the function used to write the message before reading it :
Future<void> sendMessage(String id) async {
DocumentReference documentToSave = FirebaseFirestore.instance
.collection("chats")
.doc(id)
.collection("messages")
.doc("bucket");
HashMap<String, Object> newMessage = new HashMap<String, Object>();
newMessage.putIfAbsent("body", () => "Hi,I'm a message");
await documentToSave.set(newMessage);
}
Hope this is enough.
EDIT : Solved this is the solution :
Future<String> readMessageFromChat(String chatId, int limit) async {
QuerySnapshot collectionToRead = await FirebaseFirestore.instance
.collection("chats")
.doc(chatId)
.collection("messages")
.limit(limit)
.get();
String ret = "I have read the message : ";
collectionToRead.docs.forEach((element) {
ret += element.data()["body"].toString();
});
return ret;
}
解决方案
I think your issue is mixing streams
and futures
. Calling snapshots
on your query returns a stream<QuerySnapshot>
. Your readMessageFromChat
expects to return a Future
however. Calling forEach
on a stream doesn't do anything on the stream until the stream has fired -- which will be after the execution of your function. So you'll end up returning I have read the message :
.
Your two options are to make it an awaited get()
instead of a snapshots()
or to return a Stream<String>
and modify the string as how you were doing but with a map
instead of a forEach
.
Let me know what you further intend and I'll possibly update my answer.
Also, if you want the latest one, you'll need an orderBy query modifier.
Edit
Streams can get complicated but for firestore, you're usually only ever reading from them. Streams are different from Futures as streams are continuous data. That means, at any time, a stream can give you more data. Streams are therefore listenable
. You need to setup a listener to be notified of when more data is given to you. Usually you'd have to set stuff up yourself in the widget's init state by saying addListener
and then dispose of it yourself and keep track of loading and error states yourself etc. However, instead of that you can just use a StreamBuilder
that internally does all of this for you. Here is an example from a SO post of how to do that.
A QuerySnapshot
means roughly what you think it means. It's just the data you get from asking executing a query, similar to any other database (select * from customers where ...
). QuerySnapshot also holds metadata and docs as well as docChanges which can be useful.
推荐阅读
- curl - CURL 是否可以截取参数?
- java - 如何使用 Dagger 2 在改造调用中添加授权标头
- nlp - Microsoft LUIS 无法识别实体
- mysql - 当组在另一个表上时,使用左连接技巧检索组的最大/最小行
- powershell - 修改远程计算机文件夹权限时访问被拒绝错误
- regex - 正则表达式匹配只能包含字符和数字的字符串
- r - 如何在r中转换为unix时间?
- python - Spark 2.3 Executor 内存泄漏
- javascript - 我不明白如何测试用 jest 发送 mailgun 的成功
- node.js - 如何在 Azure 中使用 Node JS 函数上传文件