My Riverpod Learnings
In this blog, I documented all my learning of Riverpod and also gave examples of every provider used in Riverpod.
Introduction
Hey everyone I am Hasnain Makada, currently building out Open Source with Hasnain where every beginner regardless of their field can find tons of resources related to Web Dev, Android, AI & ML etc... This project is also intended for beginners to contribute to it and get their journey started with Open Source. You can check out the project on GitHub.
In this blog, I'm posting my learnings related to riverpod (A state management solution). This blog contains all the necessary providers and I've also provided the steps to install riverpod and get started with it in Flutter
I've learnt about flutter_riverpod
but there are also many other riverpod packages which you can get started with it. You can explore them from the official docs
Installing Riverpod
We are going to install flutter_riverpod
in this blog so create a new flutter project by running flutter create riverpod_demo
and after the project is created successfully, run flutter pub add flutter_riverpod
and the riverpod package will be added successfully to the project.
What is Riverpod
Let me give you an overview of what riverpod is and what does it in your flutter apps,
Riverpod is a state management library for Flutter, a mobile app development framework. It allows developers to manage the state, or data, of their app in a consistent and organized way, making it easier to build and maintain the app. It uses the concept of "providers" to manage states, which are objects that hold and provide access to the app's data. This makes it easy to update and access the state in different parts of the app and ensures that the data is always in a consistent state.
One of the main features of Riverpod is that it uses "providers" to manage the state. These objects hold a piece of data and provide access to it. You can think of them as similar to global variables, but with the added benefit of being able to update the data in a consistent way.
Overall, Riverpod aims to make state management in Flutter apps more predictable, organized and easier to maintain, by providing a consistent and powerful way to manage the state throughout the application.
Now that we've discussed Riverpod, let's start with the providers which are in it.
Providers
1. Provider
Provider is the most basic type of provider in riverpod. It basically creates value and that's all about it.
Before using any provider in riverpod, wrap the MyApp
widget with the ProviderScope
widget so that we can read all our providers in our app.
runApp(
// For widgets to be able to read providers, we need to wrap the entire
// application in a "ProviderScope" widget.
// This is where the state of our providers will be stored.
ProviderScope(
child: MyApp(),
),
The use of provider is as follows,
class MyApp extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final String value = ref.watch(helloWorldProvider);
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Example')),
body: Center(
child: Text(value),
),
),
);
}
}
Riverpod provides us with ConsumerWidget
with which you can update the data in your app without rebuilding the whole widget and it also improves the performance in your app.
2. StateProvider
StateProvider
is a provider that exposes a way to modify its state. It is a simplification of StateNotifierProvider, designed to avoid having to write a StateNotifier class for very simple use cases.
The use of StateProvider
is as follows,
final nameProvider = StateProvider<String?>(
(ref) {
return null;
},
);
// It initially returns null over here
final name = ref.watch(nameProvider); // we can refrence it with the Ref inside the build mathod but make sure that the whole widget should be a consumer widget or call this only inside the consumer wrap around.
ref.watch(nameProvider.notifier).update((state) => value); // to update the values, call the provider with the .notifier parameter and it will provide you with a set of methods to update, dispose etc..
3. StateNotifier and StateNotifierProvider
StateNotifierProvider
is a provider that allows you to create and expose a StateNotifier
to your widgets. This provider can be used to observe the state of the StateNotifier
and rebuild the widgets that depend on it when the state changes.
The use of StateNotifierProvider
is as follows,
class UserNotifier extends StateNotifier<User> {
UserNotifier()
: super(
User(name: '', age: 0),
);
void updateName(String n) {
state = state.copyWith(name: n);
}
void updateAge(int age) {
state = state.copyWith(age: age);
}
}
// State Notfier Provider
final userProvider = StateNotifierProvider<UserNotifier, User>( // exposes the usernotifer to the outside world
((ref) => UserNotifier()),
);
4. FutureProvider
FutureProvider
is a provider in the Riverpod library that allows you to easily manage the state of a future in your Flutter app. It is used to provide an Future
object to a descendant widget and automatically handles the loading, error, and data states. When the future completes, the widget tree is rebuilt with the data from the future. It's a way to manage the state of an asynchronous task in your app.
The FutureProvider
implemented as follows,
class UserRepository {
Future<User> fetchUserData() {
const url1 = "https://jsonplaceholder.typicode.com/users/1";
return http.get(Uri.parse(url1)).then((value) => User.fromJson(value.body));
}
}
// Always create a separate class when working with futureproviders/ Stream Providers
final fetchUserProvider = FutureProvider(
(ref) {
return UserRepository().fetchUserData();
},
);
// Main.dart File
final user = ref.watch(fetchUserProvider);
return user.when(
data: (data) {
return Scaffold(
appBar: AppBar(title: const Text('Sample')),
body: Center(
child: Text(
style: const TextStyle(fontSize: 30),
data.name.toString(),
),
),
);
},
error: (error, stackTrace) {
return Text('Error');
},
loading: () {
return Text('Loading');
},
);
5. StreamProviders
StreamProvider
is used to provide a value that is generated by a Stream
and it is automatically updated when the Stream emits a new value.
StreamProvider
is implemented as follows,
final streamProvider = StreamProvider(
(ref) async* {
yield [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
},
);
// Main.dart file
return ref.watch(streamProvider).when(
data: (data) {
return Scaffold(
body: Center(
child: Text(
data.toString(),
),
),
);
},
error: (error, stackTrace) {
return Text("Error Found");
},
loading: () {
return Text('Loading');
},
);
Now that we have discussed the basic providers which are used in riverpod and also seen the implementation of all of them in code also. Now let's look into some of the modifiers which riverpod provides to us.
Modifiers
Riverpod mainly provides us with 2 modifiers,
.family
.autoDispose
The .family
modifier has only one purpose in a provider: Getting a unique provider based on external parameters.
Some common use-cases for family
would be:
- Combining FutureProvider with
.family
to fetch aMessage
from its ID
The .autoDispose
modifier destroys the state of the provider when it is no longer used.
There are multiple reasons for doing so, such as:
When using Firebase, close the connection and avoid unnecessary costs.
To reset the state when the user leaves a screen and re-enters it.
Provider Observers
Provider observer listens to the changes in a provider container. It is basically used to provide loggers for all the providers we use in our app.
To know more about provider observers, check out the official documentation.
Wrapping up!!!
So that was all my learnings curated inside one blog, These are just the basics of riverpod and it took me almost a week to learn and understand it. I hope that this blog will help you in some way while you are learning and you can use my blog as a reference while you are learning it. If you have any doubts related to flutter and DevOps, feel free to reach out on Twitter and Showwcase