Family Providers (Parameterized Queries)
In Riverpod, “families” are used to pass parameters to providers. While queryProvider is a factory function, you can easily create parameterized queries by wrapping the factory call in a function or a getter.
Basic Parameterized Query
Section titled “Basic Parameterized Query”The most common way to handle parameters is to define a function that returns the provider for a specific set of arguments.
import 'package:flutter_riverpod/flutter_riverpod.dart';import 'package:fasq_riverpod/fasq_riverpod.dart';
// 1. Create a function that returns the providertypedef UserProvider = AutoDisposeAsyncNotifierProvider<QueryNotifier<User>, User>;
UserProvider userProvider(String userId) { return queryProvider<User>( ['user', userId].toQueryKey(), () => api.fetchUser(userId), options: QueryOptions( staleTime: Duration(minutes: 5), ), );}
class UserProfileScreen extends ConsumerWidget { final String userId; const UserProfileScreen({required this.userId});
@override Widget build(BuildContext context, WidgetRef ref) { // 2. Watch the specific provider instance final userAsync = ref.watch(userProvider(userId));
return Scaffold( appBar: AppBar(title: Text('User Profile')), body: userAsync.when( data: (user) => Text('Name: ${user.name}'), loading: () => Center(child: CircularProgressIndicator()), error: (error, stack) => Center(child: Text('Error: $error')), ), ); }}Multiple Parameters
Section titled “Multiple Parameters”When you have multiple parameters, it’s best to group them into a custom class or record to ensure consistent caching and comparison.
// Using a record for multiple parametersfinal userPostsProvider = (String userId, int page) => queryProvider<List<Post>>( ['posts', userId, page].toQueryKey(), () => api.fetchUserPosts(userId, page),);
class UserPostsScreen extends ConsumerWidget { final String userId; final int page;
const UserPostsScreen({required this.userId, this.page = 1});
@override Widget build(BuildContext context, WidgetRef ref) { // Watching with multiple parameters final postsAsync = ref.watch(userPostsProvider(userId, page));
return postsAsync.when( data: (posts) => ListView.builder( itemCount: posts.length, itemBuilder: (context, index) => ListTile(title: Text(posts[index].title)), ), loading: () => CircularProgressIndicator(), error: (e, s) => Text('Error'), ); }}Parameterized Mutations
Section titled “Parameterized Mutations”Similarly, mutations can be parameterized if they depend on external state or specific IDs for their logic.
final updateUserProvider = (String userId) => mutationProvider<User, String>( (newName) => api.updateUser(userId, newName), options: MutationOptions( onSuccess: (data, variables) { // Invalidate the specific user query ref.invalidate(userProvider(userId)); }, ),);
// In your widget:final mutation = ref.watch(updateUserProvider(userId));
ElevatedButton( onPressed: () => ref.read(updateUserProvider(userId).notifier).mutate('New Name'), child: Text('Update'),)Dependent Queries
Section titled “Dependent Queries”You can use the result of one query to enable or parameterize another query.
class UserProfile extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final currentUserId = ref.watch(currentUserIdProvider);
// Dependent fetch: only enabled if userId is present final userAsync = ref.watch(userProvider(currentUserId ?? ''));
return userAsync.when( data: (user) => UserDetails(user), loading: () => CircularProgressIndicator(), error: (e, s) => Text('Error'), ); }}Performance & Caching
Section titled “Performance & Caching”Fasq handles the heavy lifting of caching results by their QueryKey. Even if you recreate the provider instance via a function call, as long as the QueryKey is identical and the provider is still being watched elsewhere, Fasq will return the same underlying data and Riverpod will share the same state.
[!TIP] Always include all parameters that affect the data in your
QueryKey. This ensures that different parameters don’t clobber each other’s cache.
Next Steps
Section titled “Next Steps”queryProvider- Deep dive into standard queriesmutationProvider- Learn about mutations- Caching Strategy - How keys and stale time work together