¿Estás usando una transmisión de Firebase como entrada para otra transmisión en Flutter?
Contexto: tengo dos transmisiones de Firebase que funcionan correctamente y obtienen i) una lista de perfiles de usuario (colección de 'usuarios') y ii) una lista de ubicaciones que pertenecen a cada perfil de usuario (colección de 'ubicaciones'), y luego, asígnelos a un modelo de ubicación y usuario personalizado.
Flujo de usuarios:
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);
}
y el flujo de ubicación:
// 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);
}
Lo que quiero hacer es generar una transmisión personalizada que genere todas las ubicaciones para todos los usuarios; en otras palabras, usar la transmisión de usuarios como entrada para la transmisión de ubicaciones.
No estoy seguro de qué enfoque funciona aquí; consideré agregar la transmisión de usuarios como un parámetro de entrada a la transmisión de ubicaciones y luego crear un bucle for, algo como esto:
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;
pero, por supuesto, recibo un error ya que esto devuelve una lista, no una secuencia. Así que no tengo idea de cómo proceder ...
Respuestas
Escucho tu dolor. He estado ahí. Estabas bastante cerca. Déjame explicarte cómo me gusta hacerlo.
Primero que nada, un poco de limpieza:
Parecía que no los estaba usando en las allLocations
funciones, así que los eliminé
final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;
En segundo lugar, he cambiado el tipo de retorno de la función a partir Stream<List<Location>>
de Stream<Map<String, List<Location>>
donde la clave del mapa sería el ID de usuario. Encuentro este tipo útil, porque no tienes que preocuparte por el orden de los usuarios sincronizados con la transmisión.
En tercer lugar, cuando crea transmisiones, no puede regresar, pero debe ceder el paso de una función. También tienes que marcar la función async*
(* no es un error tipográfico).
Con esto, te propongo que uses algo como esto para tu allLocations
funció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;
}
}
}
}
}
Espero que te guste este método. Por favor, avíseme si algo no es lo que desea. Puedo hacer ajustes.