Combining Queries
Combining multiple query providers into a single state is straightforward in Riverpod using standard Provider composition.
Standard Pattern: Using Provider
Section titled “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 providersfinal usersProvider = queryProvider('users'.toQueryKey(), () => api.fetchUsers());final postsProvider = queryProvider('posts'.toQueryKey(), () => api.fetchPosts());
// 2. Combine them using a simple Providerfinal 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
Section titled “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?
Section titled “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
Section titled “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
Section titled “Next Steps”queryProvider- Deep dive into standard queries- Riverpod Patterns - Architecture and best practices