Reputation: 33
I'm stuck in trying to combine two Riverpod StreamProvider. The goal is to perform a query to Firestore for fetching data, based on other data coming from Firestore, like this:
This is my database provider to share FirestoreDatabase class instance:
final databaseProvider = Provider<FirestoreDatabase>((ref) {
final auth = ref.watch(authStateChangesProvider);
if (auth.data?.value?.uid != null) {
return FirestoreDatabase(uid: auth.data!.value!.uid);
}
throw UnimplementedError();
});
Then I've created a StreamProvider to fetch user's data
final userProvider = StreamProvider<UserModel>((ref) {
final database = ref.watch(databaseProvider);
return database.getUser();
});
Now I need to create the last provider, but I can't make it works. This is what I write since now:
final nodesProvider = StreamProvider<List<Node>>((ref) {
final database = ref.watch(databaseProvider);
final user = ref.watch(userProvider);
//... some code to await user data fetching
// user.nodes contains the list I need to pass in order to perform the query
return database.getNodes(user.nodes);
});
Someone could help me or give me a hint? Thanks in advance
Upvotes: 2
Views: 3150
Reputation: 2274
From personal experience, I recommend to stay away from StreamProviders unless it's only one level deep (no streams that rely on other stream).
Instead, Riverpod has a .last
provider that exposes the last value in a stream. Calling ref.watch
will let you listen to it and any updates.
final databaseProvider = FutureProvider<FirestoreDatabase>((ref) async {
final auth = await ref.watch(authStateChangesProvider.last);
if (auth.data?.value?.uid != null) {
return FirestoreDatabase(uid: auth.data!.value!.uid);
}
throw StateError('auth uid is null so no database could be created');
});
final nodesProvider = FutureProvider<List<Node>>((ref) async {
// you can Future.wait these if you want
final database = await ref.watch(databaseProvider);
final user = await ref.watch(userProvider.last);
return database.getNodes(user.nodes);
});
I think that would work although I might have got the syntax wrong a bit (writing this in SO text area). On the other hand, there are ways to listen to streams in streams but I've never had luck with it. Here's what I think that would look like.
final databaseProvider = StreamProvider<FirestoreDatabase?>((ref) async* {
// Or any loading value really... Idk if I'd actually do this
yield null;
for await (final authState in ref.watch(authStateChangesProvider.stream)) {
if (auth.data?.value?.uid != null) {
yield FirestoreDatabase(uid: auth.data!.value!.uid);
}
}
});
final nodesProvider = StreamProvider<List<Node>?>((ref) async* {
// [Edit] You could probably use combineStream from rxDart here
for await (final database in await ref.watch(databaseProvider.stream)) {
if (database != null) {
for await (final user in ref.watch(userProvider.stream)) {
yield database.getNodes(user.nodes);
}
}
}
});
Upvotes: 2