I have a chat app in Flutter using Firestore, and I have two main collections:
chats
, which is keyed on auto-ids, and has message
,
I got another version working which seems slightly better than my answer with the two nested builders.
Here I isolated on the data loading in a custom method, using a dedicated Message
class to hold the information from a message Document
and the optional associated user Document
.
class Message {
final message;
final timestamp;
final uid;
final user;
const Message(this.message, this.timestamp, this.uid, this.user);
}
class ChatList extends StatelessWidget {
Stream> getData() async* {
var messagesStream = Firestore.instance.collection("chat").orderBy("timestamp", descending: true).snapshots();
var messages = List();
await for (var messagesSnapshot in messagesStream) {
for (var messageDoc in messagesSnapshot.documents) {
var message;
if (messageDoc["uid"] != null) {
var userSnapshot = await Firestore.instance.collection("users").document(messageDoc["uid"]).get();
message = Message(messageDoc["message"], messageDoc["timestamp"], messageDoc["uid"], userSnapshot["name"]);
}
else {
message = Message(messageDoc["message"], messageDoc["timestamp"], "", "");
}
messages.add(message);
}
yield messages;
}
}
@override
Widget build(BuildContext context) {
var streamBuilder = StreamBuilder>(
stream: getData(),
builder: (BuildContext context, AsyncSnapshot> messagesSnapshot) {
if (messagesSnapshot.hasError)
return new Text('Error: ${messagesSnapshot.error}');
switch (messagesSnapshot.connectionState) {
case ConnectionState.waiting: return new Text("Loading...");
default:
return new ListView(
children: messagesSnapshot.data.map((Message msg) {
return new ListTile(
title: new Text(msg.message),
subtitle: new Text(DateTime.fromMillisecondsSinceEpoch(msg.timestamp).toString()
+"\n"+(msg.user ?? msg.uid)),
);
}).toList()
);
}
}
);
return streamBuilder;
}
}
Compared to the solution with nested builders this code is more readable, mostly because the data handling and the UI builder are better separated. It also only loads the user documents for users that have posted messages. Unfortunately, if the user has posted multiple messages, it will load the document for each message. I could add a cache, but think this code is already a bit long for what it accomplishes.