When we last left, we defined a value notifier to let us know when to update our state. The value notifier was so closely related to our model object, a better solution would be to just have our model extend the value notifier. Let’s do this now.
To get started, open your project in progress or download the starter project. Open up pillar.dart.
The first we thing we need to do is extend our ValueNotifier. We also must indicate what type of value we are using. In our case, we are using an int.
Adjust the class definition to the following:
class Pillar extends ValueNotifier<int> {
The ValueNotifier class contains the value property which we’ll use to represent the total article count. First, adjust the constructor to initialize the value count.
Pillar({required this.type, int articleCount = 10}) : super(articleCount);
Here we set the value count to represent the current article count. Now let’s update our properties.
class Pillar extends ValueNotifier<int> {
int get articleCount => value;
var active = true;
final PillarType type;
Here we are using the value property as our backing variable. Notice we got rid of the articleCount property.
Finally, we need to update the article count. Update it to the following:
void increaseArticleCount({int by = 1}) {
value += by;
}
Now open main.dart.
We need to update our state to use our pillar variable instead of using the value notifier.
@override
void initState() {
pillarData.addListener(() {
setState(() {});
});
super.initState();
}
In this case, our setState method is empty. We are indicating that our state change, thus we need to rebuild the user interface. But we aren’t specifying what changed. It’s a clunky solution. Now update dispose.
@override
void dispose() {
pillarData.dispose();
super.dispose();
}
Finally, let’s remove all the places we’re passing in the value notifier.
body: TutorialsPage(pillar: pillarData),
Open tutorials_page.dart. Remove the ValueNotifier. Update the constructor.
const TutorialsPage({required this.pillar, super.key});
Then TutorialWidget to the following.
child: TutorialWidget(pillar: widget.pillar),
Now open tutorial_widget.dart. Remove the ValueNotifier.
const TutorialWidget({required this.pillar, super.key});
In the _TutorialWidgetState, update to onTap to the following:
widget.pillar.increaseArticleCount(by: 1);
Our app is now up to date.
Build and run. Tap the button.
Our tutorial count updates as expected. It works but you should avoid empty setState calls. Thankfully, there’s a better way and that’s the ValueListenableBuilder. This is a widget that listens to our value notifier and automatically rebuild the widget tree based on a change in the value notifier.
In main.dart, select the TutorialsPage widget and wrap in another widget. Update it to the following:
ValueListenableBuilder(
valueListenable: pillarData,
builder: (context, value, child) {
return TutorialsPage(pillar: pillarData);
}),
We pass in our pillarData as the listenable. Next we use a builder to create the TutorialsPage. It takes three variables: the context, a value, and a child. The context is a plain old context. That is, the current widget location in the widget tree.
The value represents the changed value of the value notifier. Finally the child is a widget subtree that you might you use. This is useful when you want to update a small part of the ui without having to rebuild the entire thing. This is useful for performance reasons. With a ValueListenableBuilder in place, delete the initState and dispose methods. Now build and run. Tap the tutorial counter. Everything updates as before except this time, our code is cleaner and concise.