# Flutter Riverpod Tutorial Part 3: Combining Providers

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**

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.

## **Managing User and Post Data**

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.

1. ### **Add User Model**
    

Let's create a file `user.dart`

```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'],);
  }
}
```

2. ### **Add Post Model**
    

Let's create another file `post.dart`

```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'],
    );
  }
}
```

3. ### **Create our User Provider**
    

I am updating our `basic_providers.dart` to include this provider. This is similar to [Future Provider from Lesson 2](https://harishkunchala.com/flutter-riverpod-tutorial-part-2-asynchronous-providers-futureprovider-and-streamprovider#heading-step-2-create-a-futureprovider-for-fetching-data). The only difference is that instead of returning a List of Json we are returning `List<User>>`

```dart
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');
  }
});
```

4. ### **Create Selected User Provider**
    

Let's use `StateProvider` to manage the selected user:

```dart

final selectedUserProvider = StateProvider<User?>((ref)=> null);
```

5. ### **Create Post by User Provider**
    

Let's create a `postsByUserProvider` that depends on `selectedUserProvider`

```dart
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');
  }
});
```

6. ### **Use Combined Providers in our**`UsersPostPage`
    

Let's create `users_posts_page.dart` to display users and posts based on the user:

```dart
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(),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
```

7. ### Output:
    

And finally here's the output:

![Combined Posts Provider](https://media4.giphy.com/media/v1.Y2lkPTc5MGI3NjExNDZ1dXpzZGhmeXU1M2NoOHp6ZXRjZWk5dGozMjhjanV3YjIwYXZpeSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/evqi3hS4iaRhTX7KRl/giphy.gif align="center")

8. ### Source Code:
    

You can find the source code for the app here: [riverpod\_combining\_providers](https://github.com/khkred/go_router_riverpod_tutorials/tree/riverpod_combining_providers)
