Sử dụng Luồng Firebase làm đầu vào cho Luồng khác trong Flutter?
Bối cảnh: Tôi có hai Luồng Firebase hoạt động chính xác và chúng tìm nạp i) danh sách hồ sơ người dùng (tập hợp 'người dùng') và ii) danh sách các vị trí thuộc từng hồ sơ người dùng (tập hợp 'địa điểm') và sau đó ánh xạ chúng sang mô hình Vị trí và Người dùng tùy chỉnh.
Luồng người dùng:
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);
}
và Luồng vị trí:
// 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);
}
Những gì tôi muốn làm là tạo một Luồng tùy chỉnh xuất ra tất cả các vị trí cho tất cả người dùng - nói cách khác là sử dụng luồng người dùng làm đầu vào cho luồng vị trí.
Tôi không chắc cách tiếp cận nào hoạt động ở đây - tôi đã xem xét việc thêm luồng người dùng làm thông số đầu vào cho luồng vị trí và sau đó tạo vòng lặp, tương tự như thế này:
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;
nhưng tất nhiên tôi gặp lỗi vì nó trả về một danh sách chứ không phải một luồng. Vì vậy, tôi không biết làm thế nào để tiếp tục ...
Trả lời
Tôi nghe thấy nỗi đau của bạn. Tôi đã ở đấy. Bạn đã khá gần. Hãy để tôi giải thích cách tôi thích làm điều đó.
Trước hết, một số dọn dẹp:
Có vẻ như bạn đã không sử dụng chúng trong các allLocations
chức năng, vì vậy tôi đã xóa chúng
final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;
Thứ hai, tôi đã thay đổi kiểu trả về của hàm từ Stream<List<Location>>
đến Stream<Map<String, List<Location>>
nơi quan trọng của bản đồ sẽ là userId. Tôi thấy loại này hữu ích, vì bạn không phải lo lắng về thứ tự của người dùng được đồng bộ hóa với luồng.
Thứ ba, khi bạn đang tạo luồng, bạn không thể quay lại mà phải nhường từ một hàm. Bạn cũng phải đánh dấu hàm async*
(* không phải là lỗi đánh máy).
Với điều này, tôi đề xuất bạn sử dụng một cái gì đó như thế này cho allLocations
chức năng của bạn :
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;
}
}
}
}
}
Tôi hy vọng bạn thích phương pháp này. Xin vui lòng cho tôi biết nếu điều gì đó không phải là những gì bạn muốn. Tôi có thể điều chỉnh.