Combining Queries
Combining multiple query providers into a single state is straightforward in Riverpod using standard Provider composition.
Standard Pattern: Using Provider
Since queryProvider returns a standard Riverpod provider, you can use Provider or FutureProvider to combine them.
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fasq_riverpod/fasq_riverpod.dart';
// 1. Define individual providers
final usersProvider = queryProvider('users'.toQueryKey(), () => api.fetchUsers());
final postsProvider = queryProvider('posts'.toQueryKey(), () => api.fetchPosts());
// 2. Combine them using a simple Provider
final dashboardProvider = Provider<AsyncValue<DashboardData>>((ref) {
final usersState = ref.watch(usersProvider);
final postsState = ref.watch(postsProvider);
// You can return a combined AsyncValue
if (usersState.hasError) return AsyncValue.error(usersState.error!, usersState.stackTrace!);
if (postsState.hasError) return AsyncValue.error(postsState.error!, postsState.stackTrace!);
if (usersState.hasValue && postsState.hasValue) {
return AsyncValue.data(DashboardData(
users: usersState.value!,
posts: postsState.value!,
));
}
return const AsyncValue.loading();
});
class DashboardData {
final List<User> users;
final List<Post> posts;
DashboardData({required this.users, required this.posts});
}Dependent Combinations
Sometimes you only want to fetch the second query after the first one has succeeded.
final userProvider = queryProvider('user'.toQueryKey(), () => api.fetchUser());
final userPreferencesProvider = queryProvider(
'prefs'.toQueryKey(),
() async {
final user = ref.read(userProvider).value;
return await api.fetchPreferences(user!.id);
},
// Only enable if user is loaded
options: QueryOptions(
enabled: ref.watch(userProvider).hasValue,
),
);Why no combineQueries?
While some libraries provide a specific combineQueries function, Riverpod’s native composition is more powerful and flexible. By using standard providers, you get:
- Granular Rebuilds: Widgets can watch only the specific part of the combination they need.
- Type Safety: No need for index-based access or casting.
- Familiar Patterns: Use the same Riverpod patterns you use for everything else.
Best Practices
- Avoid heavy logic in build: Keep your combinations in providers rather than calculating them inside widget
buildmethods. - Handle all states: Remember that when combining
AsyncValues, you need to decide how to represent the combined loading and error states. - Use
.whenon individual providers: If your UI allows it, handle the loading/error states of each query independently for a better user experience (e.g., showing parts of the dashboard while others are still loading).
Next Steps
queryProvider- Deep dive into standard queries- Riverpod Patterns - Architecture and best practices
Last updated on