Flutter Riverpod Tutorial Part 3: Combining Providers

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.
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 n...
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 will learn how to combine multiple providers to create more complex states and manage dependencies between providers in Riverpod.
Combining providers allows you to create more complex and interdependent states in your application. Riverpod makes it easy to manage dependencies between different providers. We are going to explore multiple scenarios of combining providers.
We are going to create an example where we manage both user and post data. We will simulate a scenario where posts are fetched based on the selected user.
Let's create a file user.dart
class User {
final int id;
final String name;
User({required this.id, required this.name});
factory User.fromJson(Map<String, dynamic> json) {
return User(id: json['id'], name: json['name'],);
}
}
Let's create another file post.dart
class Post {
final int id;
final String title;
final String body;
Post({
required this.id,
required this.title,
required this.body,
});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
id: json['id'],
title: json['title'],
body: json['body'],
);
}
}
I am updating our basic_providers.dart to include this provider. This is similar to Future Provider from Lesson 2. The only difference is that instead of returning a List of Json we are returning List<User>>
final usersProvider = FutureProvider<List<User>>((ref) async {
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('Failed to load user');
}
});
Let's use StateProvider to manage the selected user:
final selectedUserProvider = StateProvider<User?>((ref)=> null);
Let's create a postsByUserProvider that depends on selectedUserProvider
final postsByUserProvider = FutureProvider<List<Post>>((ref) async {
final selectedUser = ref.watch(selectedUserProvider);
if (selectedUser == null) {
return [];
}
final response = await http.get(Uri.parse(
'https://jsonplaceholder.typicode.com/posts?userId=${selectedUser.id}'));
if (response.statusCode == 200) {
final jsonData = json.decode(response.body);
return jsonData.map((post) => Post.fromJson(post)).toList();
} else {
throw Exception('Failed to load posts');
}
});
UsersPostPageLet's create users_posts_page.dart to display users and posts based on the user:
class UserPostsPage extends ConsumerWidget {
const UserPostsPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final userPosts = ref.watch(postsByUserProvider);
final usersListAsyncValue = ref.watch(usersProvider);
return Scaffold(
appBar: AppBar(
title: const Text('User Posts Page'),
),
body: SizedBox(
width: double.infinity,
child: Column(
children: [
usersListAsyncValue.when(
data: (users) {
// In here we are selecting the users based on drop down
return DropdownButton<User>(
hint: const Text('Select a User'),
items: users
.map((user) => DropdownMenuItem(
value: user, child: Text(user.name)))
.toList(),
onChanged: (user) {
ref.read(selectedUserProvider.notifier).state = user;
});
},
error: (error, stackTrace) => Center(
child: Text('Error: $error'),
),
loading: () => const Center(
child: CircularProgressIndicator(),
),
),
Expanded(
//Only when a user is selected we'll get the posts from that user
child: userPosts.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),
);
});
},
error: (error, stackTrace) => Center(
child: Text('Error: $error'),
),
loading: () => const Center(
child: CircularProgressIndicator(),
),
),
),
],
),
),
);
}
}
And finally here's the output:

You can find the source code for the app here: riverpod_combining_providers