Flutter Riverpod Tutorial Part 4: Modifier & Parameters

Search for a command to run...

No comments yet. Be the first to comment.
In this series, I'll cover every concept of Riverpod in a comprehensive manner, so that any user can understand total ins and outs of any Riverpod concept.
We are going to create a new app to use Flutter hooks. You can find the source code here: learn_flutter_hooks If you want to know more about Flutter Hooks. You can read it here: https://harishkunchala.com/flutter-hooks-everything-to-know-about-them T...
While I generally write about Flutter. This problem has been bugging me for a while and there seem to be limited resources to find a solution. So for anyone who is looking for a solution. Here you go: Type edge:flags into the address bar and open it...
In this tutorial, we’ll go through covering interceptor, which is a very crucial feature of Dio. You can find the entire source code for the project here: dio_tasker Understanding Interceptors: What is an interceptor? An interceptor in Dio is a powe...

In this part, we’ll enhance our Task Manager app by parsing JSON responses and handling different types of errors. We’ll use Dio’s built-in error handling mechanisms. You can find the source code of the entire app here: dio_tasker Parsing JSON Respon...

While building apps in Flutter is extremely fluid and wonderfully intuitive. We obviously know that we can’t build the next TikTok or Twitter by only building offline apps. Which is why we absolutely need internet capabilities in our Flutter apps. Th...

Understanding when to use setState in Flutter is crucial for managing our app’s state effectively. Here’s a detailed guide: When to use setState ? Updating the UI: Use setState when we need to update the UI in response to changes in the internal sta...

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.
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.
We will modify our existing example from Tutorial 3 to fetch posts by user ID using the family modifier.
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');
}
});
user_id_posts_page.dartFinally 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')),
),
),
],
),
);
}
}
Now we can observer the output here:

You can find the source code for Riverpod family here: riverpod family commit
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.
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');
}
});
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(),
)),
);
}
}
As we can see the posts auto dispose page works as intended

You can find the source code in this commit: Riverpod: autoDispose commit
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');
}
});
You can find the source Code for the entire tutorial in this branch: riverpod_parameters