Flutter Riverpod Tutorial Part 2: Asynchronous Providers (FutureProvider and StreamProvider)

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

FutureProvider allows us to work with Futures in our Flutter Application
It automatically handles the lifecycle of asynchronous operation.
So it provides us with a way to react to the different states of the future: loading, error and data.
http to our pubspec.yamlWe are going to look at an actual networking example by showing either a single user or list of users from JsonPlaceHolder Users API. So let's add http into our pubspec.yaml
flutter pub add http
First, we need to set up the FutureProvider. We will create a function that fetches data from the JSON Placeholder Users API and returns a list of users:
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'dart:convert'; // Required for jsonDecode
final usersProvider = FutureProvider<List<dynamic>>((ref) async {
final url = Uri.parse('https://jsonplaceholder.typicode.com/users');
final response = await http.get(url);
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to load users');
}
});
Now, let's create the UI part where we use usersProvider to display the list of users or handle loading and error states:
class UsersScreen extends ConsumerWidget {
const UsersScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final usersAsyncValue = ref.watch(usersProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Users'),
),
body: usersAsyncValue.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stackTrace) => Center(child: Text('Error: $error')),
data: (users) => ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(users[index]['name']),
subtitle: Text(users[index]['email']),
);
},
)
),
);
}
If we look closely in the above example:
usersAsyncValue.when() handles the three states:
loading: Displays a circular progress indicator.
error: Shows an error message.
data: Builds a list of users using a ListView.builder.
This is our current output:

As always you can find the source code here: https://github.com/khkred/flutter_stack/tree/future_provider
First, define a StreamProvider that sets up a stream. We'll use Dart's Stream.periodic to create a simple stream that emits an incrementing integer every second:
final counterStreamProvider = StreamProvider<int>((ref) {
return Stream.periodic(const Duration(seconds: 1), (count) => count +1);
});
This provider creates a stream that emits an incrementing count every second, starting from 1.
We will use a ConsumerWidget to consume the counterStreamProvider and update the UI every time a new value is emitted by the stream:
class CounterStreamScreen extends ConsumerWidget {
const CounterStreamScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final counterAsyncValue = ref.watch(counterStreamProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Counter Stream'),
),
body: Center(
child: counterAsyncValue.when(
data: (count) => Text('Current Count: $count', style: const TextStyle(fontSize: 24)),
loading: () => const CircularProgressIndicator(),
error: (error, stackTrace) => Text('Error: $error'),
),
),
);
}
}
In the above example:
counterAsyncValue.when() handles the different states:
loading: Shows a circular progress indicator before the first value is emitted.
error: Displays an error message if there's an issue with the stream.
data: Updates the displayed count each time a new value is emitted from the stream.
Here's our Counter Stream Screen:

And as always you can find the source code here: https://github.com/khkred/flutter_stack/tree/stream_provider
If you like to learn more about Flutter. Keep following my posts.