Saya memiliki aplikasi obrolan di Flutter menggunakan Firestore, dan saya memiliki dua koleksi utama:
chats
, Yang bersemangat pada auto-id, dan memilikimessage
,timestamp
, danuid
bidang.users
, yang dikunciuid
, dan memilikiname
bidang
Di aplikasi saya, saya menunjukkan daftar pesan (dari messages
koleksi), dengan widget ini:
class ChatList extends StatelessWidget {
@override
Widget build(BuildContext context) {
var messagesSnapshot = Firestore.instance.collection("chat").orderBy("timestamp", descending: true).snapshots();
var streamBuilder = StreamBuilder<QuerySnapshot>(
stream: messagesSnapshot,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> querySnapshot) {
if (querySnapshot.hasError)
return new Text('Error: ${querySnapshot.error}');
switch (querySnapshot.connectionState) {
case ConnectionState.waiting: return new Text("Loading...");
default:
return new ListView(
children: querySnapshot.data.documents.map((DocumentSnapshot doc) {
return new ListTile(
title: new Text(doc['message']),
subtitle: new Text(DateTime.fromMillisecondsSinceEpoch(doc['timestamp']).toString()),
);
}).toList()
);
}
}
);
return streamBuilder;
}
}
Tapi sekarang saya ingin menunjukkan nama pengguna (dari users
koleksi) untuk setiap pesan.
Saya biasanya menyebut pihak klien bergabung, meskipun saya tidak yakin apakah Flutter memiliki nama spesifik untuk itu.
Saya telah menemukan satu cara untuk melakukan ini (yang saya posting di bawah), tetapi bertanya-tanya apakah ada cara lain / lebih baik / lebih idiomatis untuk melakukan jenis operasi ini di Flutter.
Jadi: apa cara idiomatis di Flutter untuk mencari nama pengguna untuk setiap pesan dalam struktur di atas?
firebase
flutter
google-cloud-firestore
Frank van Puffelen
sumber
sumber
Jawaban:
Saya mendapatkan versi lain yang berfungsi yang tampaknya sedikit lebih baik daripada jawaban saya dengan dua pembangun bersarang .
Di sini saya mengisolasi pada pemuatan data dalam metode kustom, menggunakan
Message
kelas khusus untuk menyimpan informasi dari pesanDocument
dan pengguna terkait opsionalDocument
.Dibandingkan dengan solusi dengan pembuat bersarang kode ini lebih mudah dibaca, sebagian besar karena penanganan data dan pembuat UI lebih baik dipisahkan. Itu juga hanya memuat dokumen pengguna untuk pengguna yang telah memposting pesan. Sayangnya, jika pengguna telah memposting banyak pesan, itu akan memuat dokumen untuk setiap pesan. Saya bisa menambahkan cache, tetapi berpikir kode ini sudah agak lama untuk apa yang dicapai.
sumber
Map<String, UserModel>
dan hanya memuat dokumen pengguna satu kali.Jika saya membaca ini dengan benar, masalahnya akan menjadi: bagaimana Anda mengubah aliran data yang mengharuskan Anda membuat panggilan tidak sinkron untuk mengubah data dalam aliran?
Dalam konteks masalah, aliran data adalah daftar pesan, dan panggilan async adalah untuk mengambil data pengguna dan memperbarui pesan dengan data ini di arus.
Dimungkinkan untuk melakukan ini secara langsung dalam objek aliran Dart menggunakan
asyncMap()
fungsi. Berikut ini beberapa kode Dart murni yang menunjukkan cara melakukannya:Sebagian besar kode meniru data yang berasal dari Firebase sebagai aliran peta pesan, dan fungsi async untuk mengambil data pengguna. Fungsi penting di sini adalah
getMessagesStream()
.Kode sedikit rumit oleh fakta bahwa itu adalah daftar pesan yang masuk di arus. Untuk mencegah panggilan untuk mengambil data pengguna dari terjadi secara serempak, kode menggunakan a
Future.wait()
untuk mengumpulkanList<Future<Message>>
dan membuatList<Message>
ketika semua Futures telah selesai.Dalam konteks Flutter, Anda dapat menggunakan aliran yang berasal dari
getMessagesStream()
dalamFutureBuilder
untuk menampilkan objek Pesan.sumber
Anda dapat melakukannya dengan RxDart seperti itu .. https://pub.dev/packages/rxdart
untuk rxdart 0.23.x
sumber
f.reference.snapshots()
, karena itu pada dasarnya memuat ulang snapshot dan saya lebih suka untuk tidak bergantung pada klien Firestore yang cukup pintar untuk mendupuplikasi mereka (walaupun saya hampir yakin bahwa itu tidak terpotong).Stream<Messages> messages = f.reference.snapshots()...
, Anda bisa melakukannyaStream<Messages> messages = Observable.just(f)...
. Apa yang saya sukai dari jawaban ini adalah bahwa ia mengamati dokumen pengguna, jadi jika nama pengguna diperbarui dalam database, hasilnya langsung mencerminkan itu.Idealnya Anda ingin mengecualikan logika bisnis seperti memuat data ke layanan terpisah atau mengikuti pola BloC, misalnya:
Kemudian Anda bisa menggunakan Blok di komponen Anda dan mendengarkan
chatBloc.messages
aliran.sumber
Izinkan saya untuk mengajukan solusi RxDart versi saya. Saya menggunakan
combineLatest2
denganListView.builder
untuk membangun setiap Widget pesan. Selama pembangunan setiap pesan Widget saya mencari nama pengguna dengan yang sesuaiuid
.Dalam cuplikan ini saya menggunakan pencarian linear untuk nama pengguna tetapi itu dapat ditingkatkan dengan membuat
uid -> user name
petasumber
Solusi pertama yang saya peroleh adalah dengan membuat dua
StreamBuilder
contoh, satu untuk setiap koleksi / permintaan.Seperti yang dinyatakan dalam pertanyaan saya, saya tahu solusi ini tidak bagus, tetapi setidaknya itu berfungsi.
Beberapa masalah yang saya lihat dengan ini:
Jika Anda tahu solusi yang lebih baik, silakan posting sebagai jawaban.
sumber