Используете Firebase Stream в качестве входа для другого потока во Flutter?
Контекст: у меня есть два потока Firebase, которые работают правильно, и они извлекают i) список профилей пользователей (коллекция «пользователи») и ii) список местоположений, принадлежащих каждому профилю пользователя (коллекция «местоположения»), и затем сопоставьте их с пользовательской моделью пользователя и местоположения.
Поток пользователей:
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);
}
и поток местоположений:
// 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);
}
Я хочу создать настраиваемый поток, который выводит все местоположения для всех пользователей - другими словами, использовать поток пользователей в качестве входных данных для потока местоположений.
Я не уверен, какой подход здесь работает - я подумал о том, чтобы добавить поток пользователей в качестве входного параметра в поток местоположений, а затем создать цикл for, что-то вроде этого:
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;
но, конечно, я получаю сообщение об ошибке, поскольку это возвращает список, а не поток. Так что я понятия не имею, как действовать дальше ...
Ответы
Я слышу твою боль. Я был там. Вы были довольно близки. Позвольте мне объяснить, как мне это нравится.
Прежде всего, немного наведем порядок:
Похоже, вы не использовали их в allLocations
функциях, поэтому я удалил их.
final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;
Во- вторых, я изменил тип возвращаемого значения функции от Stream<List<Location>>
до , Stream<Map<String, List<Location>>
где ключ карты будет идента. Я считаю этот тип полезным, потому что вам не нужно беспокоиться о порядке синхронизации пользователей с потоком.
В-третьих, когда вы создаете потоки, вы не можете вернуться, но должны уступить из функции. Вы также должны отметить функцию async*
(* - это не опечатка).
При этом я предлагаю вам использовать для своей allLocations
функции что-то вроде этого :
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;
}
}
}
}
}
Надеюсь, вам понравился этот метод. Пожалуйста, дайте мне знать, если что-то не то, что вам нужно. Я могу внести коррективы.