Provider State Management In Flutter
What is the State Management Flutter?
A state is an information that can be read when the widget is built and might change or be modified over the lifetime of the app. If you want to change your widget, you need to update the state object, which can be done by using the setState() function available for Stateful widgets. The setState() function allows us to set the properties of the state object that triggers a redraw of the UI.
State management is one of the most popular and necessary processes in the lifecycle of an application. According to official documentation, Flutter is declarative. It means Flutter builds its UI by reflecting the current state of your app.
Managing State using setState() starts becoming horrific as the code grows. because whenever you need to change the widget’s UI, you have to call setState() inside the changing widget, so that it gets rebuilt, and since the application is composed of hundreds of different widgets, so there could be hundred different points where you have to take care of calling setState() and managing state. Moreover, your front-end logic will be scattered in different places in the UI code. So, using this raw technique to manage the state is not a good option, we have a better approach to managing the state, which is not just Easy but Effective, called Provider State Management.
Provider
Provider State Management, which is recommended by Google as well, mainly provides you with a central point to manage the state, and to write front-end logic.
A provider is a third-party library. Here, we need to understand three main concepts to use this library.
- ChangeNotifier
- ChangeNotifierProvider
- Consumer
ChngeNotifier
ChangeNotifier is a simple class, which provides change notifications to its listeners. It is easy to understand, implement, and optimized for a small number of listeners. It is used for the listener to observe a model for changes. In this, we only use the notifyListener() method to inform the listeners.
Let's consider the example of the hotel booking app screen required to book a hotel room. Simply we can handle the calculations in the BookingProvider class wrap with the ChangeNotifier class.
import 'dart:ffi';
import 'package:flutter/cupertino.dart';
import 'package:video_stream/models/RoomModel.dart';
class BookingProvider with ChangeNotifier {
int totalDays = 0;
int total = 0;
void setToatalDays(int days, RoomModel room) {
totalDays = days;
total = days * room.price;
notifyListeners();
}
}
ChangeNotifierProvider
ChangeNotifierProvider is the widget that provides an instance of a ChangeNotifier to its descendants. It comes from the provider package. The following code snippets help to understand the concept of ChangeNotifierProvider.
Here, we have defined a builder who will create a new instance of the BookingProvider model. ChangeNotifierProvider does not rebuild BookingProvider unless there is a need for this. It will also automatically call the dispose() method on the BookingProvider model when the instance is no longer needed.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: ChangeNotifierProvider<BookingProvider>(
builder: (_) => BookingProvider(),
child: MyApp(),
),
);
}
}
If there is a need to provide more than one class, you can use MultiProvider. The MultiProvider is a list of all the different Providers being used within its scope. Without using this, we would have to nest our Providers with one being the child of another and another. We can understand this from the below code.
Future<void> main() async {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<BookingProvider>(
create: (_) => BookingProvider()),
ChangeNotifierProvider<ThemeModel>(
create: (_) => ThemeModel(theme: ThemeModel.light)),
],
child: const MyApp(),
),
);
}
Consumer
It is a type of provider that does not do any fancy work. It just calls the provider in a new widget and delegates its build implementation to the builder. The following code explains it more clearly.
Consumer<BookingProvider>(
builder: (context, bookigProvider, child) {
return Padding(
padding: const EdgeInsets.only(bottom: 12, left: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
child:
Texts.subheads('Total', themeModel.priceColor),
),
Card(
elevation: 2,
color: Colors.amber,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 6, horizontal: 8),
child: Row(
children: [
Texts.subheads(
"${bookigProvider.total.toString()} LKR",
themeModel.textColor),
],
),
),
),
],
),
);
}),
In the above example, you can see that the consumer widget only requires a builder function, which is called whenever the ChangeNotifier changes. The builder function contains three arguments, which are context, bookingProvider, and child. The first argument, context, contains every build() method. The second argument is the instance of the ChangeNotifier, and the third argument is the child that is used for optimization. It is the best idea to put the consumer widget as deep in the tree as possible.
Awesome So that’s all from it. If you really found this article useful and if you want to learn more touch with on GitHub.