As you know, setState is the method we call to let Flutter know that our state has changed. But in the previous example, our user interface only changed in one area when we want it change everywhere.
We solved the first part of this issue by lifting state up. Now instead of of calling set state lower in the widget tree, we can call it higher. One way can do that is by Value Notifier.
Value Notifier is a tool included with the Flutter framework. Value Notifier contains a single value of the type you designate.
When you change the value, we get notified via a callback to which we can run our code. There’s a few things to note.
First of all, value provider will only notifier its listeners when its containing value changes not when it updates. Meaning, if the value notifier was set to ten and it was updated again to ten, it wouldn’t notify its listeners since the value didn’t change.
Second, once you are done with the ValueNotifier, you must dispose of it by calling its dispose method. By calling dispose, all of the listeners are removed from the object. Every State object has its own dispose method so its prime place to use when disposing our notifiers.
To get started, open your project in progress or download the starter project. We need to add a value notifier to our app. We don’t need to import a dependency. It comes free with the framework.
Open main.dart.
In our _ApplicationState, we’re going to add a property.
Add the following:
late ValueNotifier<int> valueNotifier;
Here we’ve defined a notifier but didn’t set it up. We will initialize it based on the amount of articles. Since we can’t do this right away, we set the variable to late, indicating we are going to initialize it a little bit later.
A good place to do this is in initState. This is called once for each state object that is created. Add the following:
@override
void initState() {
super.initState();
}
Since this is an inherited method, we need to override it. Now, create the value notifier.
void initState() {
super.initState();
valueNotifier = ValueNotifier<int>(pillarData.articleCount);
}
We actually have more work to do in initState, but we’ll get back to it in a moment. First, we have to pass the value notifier down the widget tree. We’re utilizing dependency injection which is a clunky approach as discussed in the previous episode. Don’t worry, we’ll fix it soon enough.
Open tutorial_widget.dart. Update it to the following:
final ValueNotifier<int> valueNotifier;
const TutorialWidget(
{required this.pillar, required this.valueNotifier, super.key});
Naturally, we have do the same for tutorial page widget.
Open tutorials_page.dart. Update the constructor.
final ValueNotifier<int> valueNotifier;
const TutorialsPage(
{required this.pillar, required this.valueNotifier, super.key});
Then, pass it into the TutorialWidget.
TutorialWidget(
pillar: widget.pillar, valueNotifier: widget.valueNotifier),
Finally, open main.dart. Pass the value notifier into the TutorialsPage.
TutorialsPage(pillar: pillarData, valueNotifier: valueNotifier),
Now time to add our listener. We will add it in initState. The addListener method takes in a closure. Add the following:
valueNotifier.addListener(() {
});
Now, inside the closure, call setState. First, we need to determine the increment amount. We do this by subtracting the new value by the total article count.
setState(() {
final increaseAmount = valueNotifier.value - pillarData.articleCount;
});
At this point, we can adjust our total article amount. Add the following:
pillarData.increaseArticleCount(by: increaseAmount);
We’ve setup our listener. Our next step is to remove our listeners when we are done with it. We do this in dispose. Add the following:
@override
void dispose() {
valueNotifier.dispose();
super.dispose();
}
This overrides the State’s dispose method whereby we call the dispose method on the value notifier. Thereby all the listeners are purged. After which, we call the super’s dispose method.
Now we need to call the value notifier.
Open tutorial_widget.dart. In onTap, increase the value notifier.
onTap: () {
widget.valueNotifier.value += 1;
},
And that’s it. Build and run. Now tap on the button. This time everything updates with your state. So, this allows our user interface to match our state. Right now, the value notifier by all account represents our article count. A better solution is to combine the value notifier with out model class and we’ll do that in the next episode.