Vous utilisez un flux Firebase comme entrée pour un autre flux dans Flutter?
Contexte: j'ai deux flux Firebase qui fonctionnent correctement, et ils récupèrent i) une liste de profils utilisateur (collection 'users'), et ii) une liste d'emplacements appartenant à chaque profil utilisateur (collection 'locations'), et puis mappez-les à un modèle personnalisé d'utilisateur et d'emplacement.
Flux d'utilisateurs:
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);
}
et le flux de localisation:
// 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);
}
Ce que je veux faire, c'est générer un flux personnalisé qui génère tous les emplacements pour tous les utilisateurs - en d'autres termes, utiliser le flux d'utilisateurs comme entrée pour le flux d'emplacements.
Je ne sais pas quelle approche fonctionne ici - j'ai envisagé d'ajouter le flux d'utilisateurs en tant que paramètre d'entrée au flux d'emplacements, puis de créer une boucle for, quelque chose comme ceci:
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;
mais bien sûr, j'obtiens une erreur car cela renvoie une liste, pas un flux. Donc je ne sais pas comment procéder ...
Réponses
J'entends ta douleur. J'ai été là-bas. Vous étiez assez proche. Laissez-moi vous expliquer comment j'aime le faire.
Tout d'abord, un peu de nettoyage:
Il semblait que vous ne les utilisiez pas dans les allLocations
fonctions, alors je les ai supprimés
final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;
Deuxièmement, j'ai changé le type de retour de la fonction Stream<List<Location>>
à l' Stream<Map<String, List<Location>>
endroit où la clé de la carte serait le userId. Je trouve ce type utile, car vous n'avez pas à vous soucier de l'ordre des utilisateurs synchronisés avec le flux.
Troisièmement, lorsque vous créez des flux, vous ne pouvez pas revenir, mais devez céder à une fonction. Vous devez également marquer la fonction async*
(* n'est pas une faute de frappe).
Avec cela, je vous propose d'utiliser quelque chose comme ça pour votre allLocations
fonction:
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;
}
}
}
}
}
J'espère que vous aimez cette méthode. S'il vous plaît laissez-moi savoir si quelque chose ne vous convient pas. Je peux faire des ajustements.