Table of contents
In this tutorial we'll explore various modifiers and parameters functionality in Riverpod. These following features help manage more complex state scenarios and optimize resource usage by automatically disposing of providers when they are no longer needed.
Family:
The family
modifier allows you to create a provider that takes parameters. This is useful when you need to create multiple instances of a provider with different parameters.
Example: Fetching Posts by User ID with Family
We will modify our existing example from Tutorial 3 to fetch posts by user ID using the family
modifier.
Create Posts by User ID Provider Using Family
Let's update our basic_providers.part
to include a provider with the family
modifier:
//Provider with family modifier
final postsByUserIdProvider =
FutureProvider.family<List<Post>, int>((ref, userId) async {
final response = await http.get(
Uri.parse('https://jsonplaceholder.typicode.com/posts?userId=$userId'));
if (response.statusCode == 200) {
List jsonData = json.decode(response.body);
return jsonData.map((post) => Post.fromJson(post)).toList();
} else {
throw Exception('Unable to fetch posts from $userId');
}
});
Use Posts by User ID Provider in our
user_id_posts_page.dart
Finally let's create the above page. Where we'll get the userId from DropDown and then we'll show the posts
class UserIdPostsPage extends ConsumerWidget {
const UserIdPostsPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final usersListAsync = ref.watch(usersProvider);
final selectedUser = ref.watch(selectedUserProvider);
final postsAsyncValue = selectedUser == null
? const AsyncValue.data([])
: ref.watch(postsByUserIdProvider(selectedUser.id));
return Scaffold(
appBar: AppBar(
title: const Text('Users and Posts with Family'),
),
body: Column(
children: [
usersListAsync.when(
data: (users) {
return DropdownButton<User>(
hint: const Text('Select a User'),
value: selectedUser,
onChanged: (user) {
ref.read(selectedUserProvider.notifier).state = user;
},
items: users.map((user) {
return DropdownMenuItem<User>(
value: user,
child: Text(user.name),
);
}).toList(),
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Text('Error: $error'),
),
Expanded(
child: postsAsyncValue.when(
data: (posts) {
return ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
final post = posts[index];
return ListTile(
title: Text(post.title),
subtitle: Text(post.body),
);
},
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text('Error: $error')),
),
),
],
),
);
}
}
Output:
Now we can observer the output here:
Source Code
You can find the source code for Riverpod family here: riverpod family commit
AutoDispose:
The AutoDispose
modifier ensures that the provider is automatically disposed of when it is no longer needed.
This is useful for optimizing resource usage, particularly for providers that hold onto resources like network connections or subscriptions.
Example: Fetching Posts by AutoDispose Provider
Create an AutoDispose Provider
Let's update our basic_providers.dart
to include an autoDispose
provider:
final autoDisposePostsProvider =
FutureProvider.autoDispose<List<Post>>((ref) async {
final response =
await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
if (response.statusCode == 200) {
List jsonData = json.decode(response.body);
return jsonData.map((post) => Post.fromJson(post)).toList();
} else {
throw Exception('Failed to load posts');
}
});
Use AutoDispose Provider in a Page
Let's create posts_auto_dispose_page.dart
to call our autoDispose
provider
class PostsAutoDisposePage extends ConsumerWidget {
const PostsAutoDisposePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final postsAutoDisposeAsyncValue = ref.watch(autoDisposePostsProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Posts with AutoDispose'),
),
body: postsAutoDisposeAsyncValue.when(
data: (posts) => ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
final post = posts[index];
return ListTile(
title: Text(post.title),
subtitle: Text(post.body),
);
},
),
error: (error, stackTrace) => Center(
child: Text("Error: $error"),
),
loading: () => const Center(
child: CircularProgressIndicator(),
)),
);
}
}
Output:
As we can see the posts auto dispose page works as intended
Source Code:
You can find the source code in this commit: Riverpod: autoDispose commit
keepAlive:
The keepAlive
modifier can be used alongside autoDispose
.
It allows certain instances of the provider to be kept alive even when not in use, based on a condition.
keepAlive
with autoDispose
:
We will create an autoDispose
Users provider to keep it alive under certain conditions.
final autoDisposeKeepAliveUsersProvider =
FutureProvider.autoDispose<List<User>>((ref) async {
//Keeps this provider alive even when it's no longer being listened to
ref.keepAlive();
final response =
await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));
if (response.statusCode == 200) {
List jsonData = json.decode(response.body);
return jsonData.map((user) => User.fromJson(user)).toList();
} else {
throw Exception('Unable to load users');
}
});
Source Code:
You can find the source Code for the entire tutorial in this branch: riverpod_parameters