Menggunakan Aliran Firebase sebagai masukan untuk Aliran lain di Flutter?
Konteks: Saya memiliki dua Aliran Firebase yang berfungsi dengan benar, dan mereka mengambil i) daftar profil pengguna (koleksi 'pengguna'), dan ii) daftar lokasi milik setiap profil pengguna (koleksi 'lokasi'), dan lalu petakan mereka ke model Pengguna dan Lokasi khusus.
Aliran pengguna:
class DatabaseService {
final String uid;
final String friendUid;
final String locationId;
DatabaseService({ this.uid, this.locationId, this.friendUid });
// collection reference for users
final CollectionReference userCollection = FirebaseFirestore.instance.collection('users');
// get users stream
Stream<List<CustomUserModel>> get users {
final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;
List<CustomUserModel> userList = [];
List<CustomUserModel> _streamMapper(DocumentSnapshot snapshot) {
CustomUserModel individualUser = CustomUserModel(
uid: snapshot.id,
name: snapshot.data()['name'],
username: snapshot.data()['username'],
email: snapshot.data()['email'],
);
userList.add(individualUser);
return userList;
}
return userCollection.doc(uid).snapshots().map(_streamMapper);
}
dan Arus Lokasi:
// collection reference for location
final CollectionReference locationCollection =
FirebaseFirestore.instance.collection('locations');
Stream<List<Location>> get locations {
final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;
List<Location> _locationListFromSnapshot(QuerySnapshot snapshot) {
List<Location> locationList = [];
snapshot.docs.forEach((element) {
Location individualLocation = Location(
locationId: element.id,
locationName: element.data()['locationName'],
city: element.data()['city'],
);
locationList.add(individualLocation);
});
return locationList;
}
return userLocationCollection.doc(uid).collection('locations').snapshots()
.map(_locationListFromSnapshot);
}
Yang ingin saya lakukan adalah menghasilkan Arus khusus yang mengeluarkan semua lokasi untuk semua pengguna - dengan kata lain menggunakan arus pengguna sebagai masukan untuk aliran lokasi.
Saya tidak yakin pendekatan apa yang berhasil di sini - Saya mempertimbangkan untuk menambahkan aliran pengguna sebagai parameter input ke aliran lokasi dan kemudian membuat for-loop, seperti ini:
Stream<List<Location>> allLocations(Stream<List<CustomUserModel>> users) {
final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;
List<Location> locationList = [];
users.forEach((element) {
// append user's locations to empty list
locationList.add(locationCollection.doc(element.first.uid).collection('locations')
.snapshots().map(SOME FUNCTION TO MAP A DOCUMENT SNAPSHOT TO THE CUSTOM LOCATION MODEL)
}
return locationList;
tapi tentu saja saya mendapatkan kesalahan karena ini mengembalikan daftar, bukan aliran. Jadi saya tidak tahu bagaimana melanjutkan ...
Jawaban
Saya mendengar rasa sakit Anda. Saya pernah ke sana. Anda cukup dekat. Izinkan saya menjelaskan bagaimana saya suka melakukannya.
Pertama-tama, beberapa pembersihan:
Sepertinya Anda tidak menggunakan ini dalam allLocations
fungsinya, jadi saya menghapusnya
final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;
Kedua, saya mengubah jenis kembalinya fungsi dari Stream<List<Location>>
ke Stream<Map<String, List<Location>>
mana kunci peta akan userid tersebut. Menurut saya jenis ini berguna, karena Anda tidak perlu khawatir tentang urutan pengguna yang sinkron dengan arus.
Ketiga, saat Anda membuat aliran, Anda tidak dapat kembali, tetapi harus menghasilkan dari suatu fungsi. Anda juga harus menandai fungsinya async*
(* bukan salah ketik).
Dengan ini, saya sarankan Anda menggunakan sesuatu seperti ini untuk allLocations
fungsi Anda :
class DataService {
List<Location> convertToLocations(QuerySnapshot snap) {
// This is the function to convert QuerySnapshot into List<Location>
return [Location()];
}
Stream<Map<String, List<Location>>> allLocations(
Stream<List<CustomUserModel>> usersStream) async* {
Map<String, List<Location>> locationsMap = {};
await for (List<CustomUserModel> users in usersStream) {
for (CustomUserModel user in users) {
final Stream<List<Location>> locationsStream = locationCollection
.doc(user.uid)
.collection('locations')
.snapshots()
.map(convertToLocations);
await for (List<Location> locations in locationsStream) {
locationsMap[user.uid] = locations;
yield locationsMap;
}
}
}
}
}
Saya harap Anda menyukai metode ini. Tolong beri tahu saya jika ada sesuatu yang tidak Anda inginkan. Saya bisa membuat penyesuaian.