Flutter Riverpod Tutorial Part 4: Modifier & Parameters

Flutter Riverpod Tutorial Part 4: Modifier & Parameters

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.

  1. 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');
  }
});
  1. 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')),
            ),
          ),
        ],
      ),
    );
  }
}
  1. Output:

Now we can observer the output here:

Flutter Family

  1. 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

  1. 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');
  }
});
  1. 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(),
              )),
    );
  }
}
  1. Output:

As we can see the posts auto dispose page works as intended

Posts auto dispose page

  1. 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

Did you find this article valuable?

Support Harish Kunchala by becoming a sponsor. Any amount is appreciated!