问题
I am working on a group chat app with Flutter and the Firestore Plugin. Getting the data from the database and converting the snapshot into a List of Messages works totally fine. But now I want to convert the uid from the database into the username (the uids and their usernames are saved in the db). This is my code:
final CollectionReference messagesCollection =
Firestore.instance.collection('messages');
final CollectionReference usersCollection =
Firestore.instance.collection('users');
Future<String> getUsernameByUID(String _uid) async {
String username =
await usersCollection.document(uid).get().then((querySnapshot) {
return (querySnapshot.data["username"]);
});
return username;
}
List<Message> _messagesFromSnapshot(QuerySnapshot snapshot){
return snapshot.documents.map((doc) {
String username = await getUsernameByUID(doc.data["uid"]);
return Message(
text: doc.data["text"] ?? "",
username: username ?? "",
time: doc.data["time"] ?? "",
);
}).toList();
}
Stream<List<Message>> get messages {
return messagesCollection
.orderBy("time")
.snapshots()
.map(_messagesFromSnapshot);
}
The problem is in this line, because I cannot run this async code inside of the map().
String username = await getUsernameByUID(doc.data["uid"]);
Is there a solution to fix this? Thanks in advance.
回答1:
async functions must return a Future, so adding async keyword to your callback means that your List.map() call must now return a List of Futures.
You can convert a List<Future<Message>> to a List<Message> by using Future.wait:
Future<List<Message>> _messagesFromSnapshot(QuerySnapshot snapshot) async {
var futures = snapshot.documents.map((doc) async {
String username = await getUsernameByUID(doc.data["uid"]);
return Message(
text: doc.data["text"] ?? "",
username: username ?? "",
time: doc.data["time"] ?? "",
);
});
return await Future.wait(futures);
}
Of course, Future.wait returns a Future and must be awaited, so now _messagesFromSnapshot must be async as well. Asynchrony is contagious, and that then would affect any callers of _messagesFromSnapshot.
Since the messages getter returns a Stream and is already asynchronous, I believe that you instead can use Stream.asyncMap:
Stream<List<Message>> get messages {
return messagesCollection
.orderBy("time")
.snapshots()
.asyncMap(_messagesFromSnapshot);
}
回答2:
What you need is Future.forEach method. it iterates over a list and waits until all the async methods to finish before moving on to the next item in the list.
Future<List<Message>> _messagesFromSnapshot(QuerySnapshot snapshot) async {
List<Message> _messages = [];
await Future.forEach(snapshot.documents, (doc) async {
String username = await getUsernameByUID(doc.data["uid"]);
_messages.add(
Message(
text: doc.data["text"] ?? "",
username: username ?? "",
time: doc.data["time"] ?? "",)
);
});
return _messages;
}
Here is an example dartpad
来源:https://stackoverflow.com/questions/58978730/how-to-use-async-code-inside-map-flutter-firestore