Verwenden Sie einen Firebase-Stream als Eingabe für einen anderen Stream in Flutter?
Kontext: Ich habe zwei Firebase-Streams, die ordnungsgemäß funktionieren und i) eine Liste von Benutzerprofilen ('Benutzersammlung') und ii) eine Liste von Standorten abrufen, die zu jedem Benutzerprofil gehören ('Standortsammlung'), und Ordnen Sie sie dann einem benutzerdefinierten Benutzer- und Standortmodell zu.
Benutzer streamen:
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);
}
und der Location Stream:
// 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);
}
Ich möchte einen benutzerdefinierten Stream generieren, der alle Speicherorte für alle Benutzer ausgibt - mit anderen Worten, um den Benutzer-Stream als Eingabe für den Standort-Stream zu verwenden.
Ich bin mir nicht sicher, welcher Ansatz hier funktioniert. Ich habe überlegt, den Benutzer-Stream als Eingabeparameter zum Speicherort-Stream hinzuzufügen und dann eine for-Schleife zu erstellen.
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;
Aber natürlich bekomme ich eine Fehlermeldung, da dies eine Liste zurückgibt, keinen Stream. Ich habe also keine Ahnung, wie ich vorgehen soll ...
Antworten
Ich höre deinen Schmerz. Ich bin dort gewesen. Du warst ziemlich nah dran. Lassen Sie mich erklären, wie ich es gerne mache.
Zunächst einige Aufräumarbeiten:
Es schien, als würden Sie diese nicht in den allLocations
Funktionen verwenden, also habe ich sie gelöscht
final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;
Zweitens habe ich den Rückgabetyp der Funktion von dort geändert Stream<List<Location>>
, Stream<Map<String, List<Location>>
wo der Schlüssel der Karte die Benutzer-ID sein würde. Ich finde diesen Typ nützlich, da Sie sich keine Gedanken über die Reihenfolge der Benutzer machen müssen, die mit dem Stream synchronisiert sind.
Drittens können Sie beim Erstellen von Streams nicht zurückkehren, sondern müssen von einer Funktion nachgeben. Sie müssen auch die Funktion markieren async*
(* ist kein Tippfehler).
Damit schlage ich vor, dass Sie so etwas für Ihre allLocations
Funktion verwenden:
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;
}
}
}
}
}
Ich hoffe dir gefällt diese Methode. Bitte lassen Sie mich wissen, wenn etwas nicht das ist, was Sie wollen. Ich kann Anpassungen vornehmen.