Skip to main content

Flutter State Management With Provider and ChangeNotifier


Flutter state management with provider library and why it's so important? There are lots of topics in flutter which are very important but out of those state management of your flutter application is the most important and little bit complicated as well. So in this article, I will try to explain how to use the provider package for managing the state of flutter and dart application and how to access the data down the hierarchy of the widget tree without recreating the whole UI.





Basic knowledge of the state management and data flow in the Flutter





Before starting the coding I would like to explain some basics about the Flutter application so that you can understand, why it's important to manage the state of your application in the correct way.





In Flutter, everything is a widget and whenever the state of your application changes the UI is recreated the UI with the new data. Let's list out the topic or problem that we are going to explore about state management.





Challenge of State Managemen in Flutter without any model





  • If every data change recreated the application widget the primary issue is the performance.
  • The complexity of accessing the data from other widgets.
  • Problem and Complexity involved if we want to move a widget from one page to other pages, in other words from one widget tree hierarchy to another widget tree. You need to do lots of changes to move the data flow logic just to access the data.
  • There are multiple solutions to this but which are the best among that is sometimes difficult. So will try to help on that topic as well in this article.




I understand that if you are new to flutter then it's difficult to understand the logic of state management and why it's so important. It's okay from small, we don't care about the state management but as application complexity will grow you will realize that we need some model to manage the state of the application.





Just, for example, we have two pages in our application. On the first page, we have listed all item which can be added by the user and later when tabbing the user to second screen all the selected item need to show on the second page. So how you will do this?





The easy way is that we can pass the parameter while routing the user to the second page, but what if the same data is required on some other page. We have to write the same again for this page and send the data to the down the hierarchy by sending the data widget by widget at the required place.





If you don't know about how to navigate to the other page in flutter then you can refer our earlier tutorial on Page Navigation In Flutter





Prerequisite to use the Provider for State Management





If we create a flutter application then we get the default code with the counter as given below.





import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(

primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter State Management Demo'),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
_counter++;
});
}

@override
Widget build(BuildContext context) {

return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}




This default application increases the counter value when the user taps on the floating action button and show into the text in the center of the home page. So, first of all, we will break this default application in three parts to convert it into the model.





Configure the Your Flutter Project to use Provider Package





First of all, we need to add the provider library as a dependency in project pubspec.yaml





dependencies:
flutter:
sdk: flutter
provider:




After adding the above line click on get package to add it as a dependency in your project.





Create a Counter Model with ChangeNotifier





So we are now going to create a model which will we responsible for providing the updated value to our widget and whenever value counter value change due to any event it will notify all the respective widgets to recreate with the latest value.





There two functions inside this model one will increase the value and others will decrease the value of the counter.





import 'package:flutter/foundation.dart';

class MyCounter with ChangeNotifier{
//internal counter
int _counter=0;
//This is getter function
int get counter => _counter;
//This is setter and set the value and notify about change
set counter(int counter){
_counter=counter;
notifyListeners(); }
//increment function
increment(){
counter=counter+1;
}
//decrement function
decrement(){
counter=counter-1;
}
}




If you notice in the above code we use the notifyListeners() function and this function is responsible for notifying all the widget about the change whenever counter value will change. This function comes from the foundation.dart package. So don't forget to import this function.





Create a Stateless Widget for our home page to demonstrate Flutter State Management with Provider





let's create a stateless widget with two-button and text to show the counter value.





class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);

final String title;

@override
Widget build(BuildContext context) {

return Scaffold(
appBar: AppBar(

title: Text("$title"),
),
body: Center(

child: Column(

mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'',
style: Theme.of(context).textTheme.display1,
),
RaisedButton(
onPressed: (){ } ,

color: Colors.blue,

child:Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(Icons.add,color: Colors.white,),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0,horizontal: 16.0),
child: Text("Increment",style: TextStyle(color: Colors.white,fontSize: 20)),
)
],
),
),
RaisedButton(onPressed: (){ },
color: Colors.blue,
child:Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(Icons.remove,color: Colors.white,),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0,horizontal: 16.0),
child: Text("Decrement",style: TextStyle(color: Colors.white,fontSize: 20),),
)
],
),
),
],
),
),);
}
}




When you click on the increment and decrement function then it will do nothing because there is no event added to it on the pressed section of the raised button.





Flutter State Management Demo




Now time to add the provider to our application in the main section of our app. There are two types of providers a single provider in which you can add the only one provider and another one is multi-provider.





We are using the multi-provider in this example but depending on your requirement you can use any of this.





If you check the default application then it's returning a material app. Now we will wrap this inside a provider like below.





class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (BuildContext context) => MyCounter(),
),
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter State Management Demo'),
),
);
}
}




The important line in the above code is given below, this line of code provides access of counter value to all of our widget as well as access to all of the function inside our counter class.





return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (BuildContext context) => MyCounter(),
),
],




How to use the Change Notifier Provider in the flutter





As mentioned above we can use the Change Notifier Provider directly or we can use it inside the MultiProvider widget. The question that comes in the mind is when to use MultiProvider and when to use Change Notifier Provider only?





The answer to the above question is very simple if you have to listen for only one state then the requirement is to get fulfilled with Change Notifier Provider only, but in case you need to listen for multiple states then you need to use MultiProvider.





Syntax to use the Change Notifier Provider





ChangeNotifierProvider(
create: (BuildContext context) => MyCounter(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter State Management Demo'),
),
);




If you are using the Page Router and not able to get the provider data then you have problem with the context. So make sure to provide the correct context at the time of using it. There is one simple solution of that if you want to listen it on multiple page.

You just need to remove the context in above syntax create: (BuildContext context) to create: (_) => MyCounter(). Then you will be able to access it across the application even if you use the Page Router.




Use the Change Notifier Provider in our application





For using the value we just need to call the provider of method like below.





 Text(
'${Provider.of<MyCounter>(context).counter}',
style: Theme.of(context).textTheme.display1,
),




How to call the function of the Model using the provider





For calling the increment and decrement function of MyCounter model we can use it as given below.





Provider.of<MyCounter>(context,listen: false).increment()
Provider.of<MyState>(context,listen: false).increment();




Now test the application you will see that the value is getting updated on click on increment and decrement button.





So, are we are able to flow the data from the top to bottom and we can use this value anywhere in our widget hierarchy. So our major problem with data access has been solved.





But there is a problem with it still our whole screen is getting rebuild after every increment and decrement.





How to check the application UI rebuild status for flutter state management with provider





So for checking this just tab on performance option in the right bottom corner of the android studio and check on show widget rebuild information inside the widget rebuild stats.









If you see in the above screenshots then you can see that on every click our home page is getting rebuild. It's fine for a small application like this but what if we have lots widget which is very heavy. It will impact the performance of our application very badly.





Use the Consumer widget to get out of the UI rebuild problem





The solution to this problem is wrapping our text widget with a Consumer widget like this.





 Consumer<MyCounter>(
builder: (BuildContext context, MyCounter value, Widget child) =>
Text(
'${${value.counter}',
style: Theme.of(context).textTheme.display1,
),
),




But after doing this as well our problem is not resolved and full UI is getting a rebuild. It is because we are still using the provider directly and listening to it in the raise button.





Now stop listing the provider method inside the raised button. For stoping this just add the listener: false in your button like below.





  RaisedButton(onPressed: (){  Provider.of<MyCounter>(context,listen: false).decrement();},




Now test it again you will see that only your text widget is getting rebuild and rest of the widget in your hierarchy will remain unchanged. Look at the below screenshot.









Understanding the listen parameter of the provider





The listen parameter is a very useful and important parameter of the Provider.of function. It tells the interface to whether to rebuild this widget or not on value change. Like, in this case, we don't need to rebuild the button on value change, we only need to rebuild the text view which showing the current counter value. So we have set the listen: false.





it's important to mention that default value of listen parameter is true. So make sure to make it false if you don't need to rebuild the widget on value change.





That's all in this article below is the full code of the above example for your reference.





Full Source code of basic Flutter State Management with Provider





import 'package:flutter/foundation.dart';

class MyCounter with ChangeNotifier{
//internal counter
int _counter=0;
//This is getter function
int get counter => _counter;
//This is setter and set the value and notify about change
set counter(int counter){
_counter=counter;
notifyListeners(); }
//increment function
increment(){
counter=counter+1;
}
//decrement function
decrement(){
counter=counter-1;
}
}




import 'package:flutter/material.dart';

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:provider/provider.dart';

import 'counter.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (BuildContext context) => MyCounter(),
),
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter State Management Demo'),
),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);

final String title;

@override
Widget build(BuildContext context) {

return Scaffold(
appBar: AppBar(

title: Text("$title"),
),
body: Center(

child: Column(

mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Consumer<MyCounter>(
builder: (BuildContext context, MyCounter value, Widget child) =>
Text(
'${value.counter}',
style: Theme.of(context).textTheme.display1,
),
),
RaisedButton(
onPressed: (){ Provider.of<MyCounter>(context,listen: false).increment(); } ,

color: Colors.blue,

child:Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(Icons.add,color: Colors.white,),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0,horizontal: 16.0),
child: Text("Increment",style: TextStyle(color: Colors.white,fontSize: 20)),
)
],
),
),
RaisedButton(onPressed: (){ Provider.of<MyCounter>(context,listen: false).decrement();},
color: Colors.blue,
child:Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(Icons.remove,color: Colors.white,),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0,horizontal: 16.0),
child: Text("Decrement",style: TextStyle(color: Colors.white,fontSize: 20),),
)
],
),
),
],
),
),);
}
}




Some reference to Provider










Comments

  1. Hi there, I read your blogs on a regular basis. Your humoristic style is witty, keep it up! Thank You for Providing Such a Unique and valuable information, If you are looking for the best Flutter App Development,then visit Neebal. I enjoyed this blog post.


    ReplyDelete
  2. You have written impressive tutorials on flutter, I really commend your work. Flutter is becoming more and more popular by mobile application developers. Thank you for sharing this, keep up the good work. Flutter and Dart training in Chennai

    ReplyDelete
  3. Hey, I enjoyed this blog. I love the way you talked about how the income of website development freelancers can be increased with the help of innovative skills. Today, several freelancing platforms can help developers earn handsome income from the comfort of their homes. Eiliana.com is a platform where developers can find work from global clients and boost their income.

    ReplyDelete
  4. I generally check this kind of article and I found your article which is related to my interest. Genuinely it is good and instructive information about Flutter App Development. Thankful to you for sharing an article like this.

    ReplyDelete
  5. Your blog's information is completely educational and useful. Everything mentioned therein is accurate. customer relationship management in erp

    ReplyDelete

Post a Comment

Popular posts from this blog

Flutter How to Start Android Activity from Flutter View

Flutter and Dart is an excellent combination for creating the UI, but for accessing the platform-specific service we need to open platform-specific activity. So lets in this article we will explore how to start an android activity and access the service from Flutter View. Create a Project for this Android Activity Flutter View Demo Create a Project From File menu select the New Flutter Project Enter the project name Select the AndroidX support and click on next After the above, we step click on Finish We will have the following project structure created. Create the Second Activity in Android Just go to the android folder and open it in separate windows. We will have the following project structure. Create the Activity Just right-click on the Kotlin folder and create a blank activity from the menu. If you create the activity then you may be required to upgrade the Gradle and do some import. So Just click on update and wait for the project s

Kotlin Parcelable Array Objects Send To Activity

We know that IPC (Inter Process Communication) between the activity is an extremely important part of any application development. We often required that we need to send some data to other activity. For example, we may be required to send an array of data, data could be an Integer, String, Long, Double, Float or any other custom data objects. So, In this example, we are going to learn how to implement the Kotlin Parcelable Array object to send the data from one activity to second activity. What is Parcel? The parcel class is designed as a high-performance IPC transport. A Parcel can contain both flattened data that will be unflattened on the other side of the IPC, and references to live IBinde r objects that will result in the other side receiving a proxy IBinder connected with the original IBinder in the Parcel. Create Kotlin Parcelable Array Objects Parcelable is API for placing the arbitrary objects into the Parcel. In Actual in android app development, Parcelable is an interface

Create Custom EditText View in Android

We use the EditText for taking the input from the user and use it at several places in our project. We required to do lots of customization for each time and there are lots of redundant code we write. Writing and managing these redundant codes is very difficult for example if we want to change the look and feel of the view them we need to modify it at each place where our EditText is getting used. So to avoid these kinds of the problem we can create our own Custom EditText View by just. The EditText view is just an extension of the TextView with lots of editing option and properties that required for the user input. How To Create Custom EditText View For creating the Custom EditText we need to extend the AppCompatEditText and override all three constructors of the view as given below. import android.content.Context; import android.graphics.Typeface; import android.support.annotation.Nullable; import android.support.v7.widget.AppCompatEditText; import android.util.AttributeSet; public