首页 > 解决方案 > 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;
  }

标签: firebaseflutterdartgoogle-cloud-firestore

解决方案


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.


推荐阅读