ChangeNotifierProvider
on Wednesday, 5th of August, 2020
Most of the examples you'll see on the internets is using the ChangeNotifierProvider, and it's also the class you'll likely use most often. This class is basically a provider-wrapper over a class that implements ChangeNotifier.
According to the Flutter docs, a ChangeNotifier is 'a class that can be extended or mixed in that provides a change notification API using VoidCallback for notifications.' In practical terms, other objects can listen to a ChangeNotifier object. And, when the change notifier gets updated values, it can call a method called 'notifyListeners()', and then any of it's listeners will respond with an action. Listening to a change notifier is done by registering a callback, which is called when notifyListeners is invoked.
That description felt a bit esoteric, so let's just look at a quick ChangeNotifier example without Provider.
class Person with ChangeNotifier {
Person({this.name, this.age});
final String name;
int age;
// when `notifyListeners` is called, it will invoke
// any callbacks that have been registered with an instance of this object
// `addListener`.
void increaseAge() {
this.age++;
notifyListeners();
}
}
void main() {
var person = Person('Yohan', 25);
// `addListener` is a method on the `ChangeNotifier` class,
// which is mixed-in to the Person class
person.addListener(() {
print('value updated!: ${person.age}')
});
person.increaseAge(); // 26
}In this example, we've started listening to the Person with ChangeNotifier class by calling addListener, which accepts a VoidCallback function as it's argument. When the age is increased, it will execute that callback.
This is one way to encapsulate the state of your app inside of classes, but it presents a problem... if you wanted to use it in multiple widgets in different branches in your widget tree, it would quickly make your code super hairy and difficult to refactor. You'd basically be passing an instance of your ChangeNotifier all around the widget tree manually.
But, that is the exact problem that provider solves.
ChangeNotifierProvider
This example is not going to be much different than the previous Provider lesson. The ChangeNotifierProvider is used exactly like the vanilla Provider. Let's start with the code, and they'll I'll highlight some important points.
First, add the provider to the tree:
class Person with ChangeNotifier {
Person({this.name, this.age});
final String name;
int age;
void increaseAge() {
this.age++;
notifyListeners();
}
}
// here, you can see that the [ChangeNotifierProvider]
// is "wired up" exactly like the vanilla [Provider]
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => Person(name: "Yohan", age: 25),
child: MyApp(),
),
);
}As you can see, wiring up a ChangeNotifierProvider is exactly the same as the Provider example from the previous lesson. Using it in widget tree is going to be similar:
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key}) : super(key: key);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Provider Class'),
),
body: Center(
child: Text(
// reading this data is exactly like it was in
// the previous lesson
'''
Hi ${Provider.of<Person>(context).name;}!
You are ${Provider.of<Person>(context).age} years old''',
),
),
floatingActionButton: FloatingActionButton(
// this is where there's a difference.
// when the FAB is tapped, it will call `Person.icreaseAge()` on the
// person instance that was created by provider.
onPressed: () => Provider.of<Person>(context, listen: false).increaseAge(),
),
);
}
}Two important notes about that code:
- Because this is a
ChangeNotifier, the UI will re-build and the age will be increased. (Success!) - You may have noticed
listen: falsein theofmethod. Thelisten: falseparameter is mandatory whenever you're using Provider to fetch an instance and call a method on that instance. This has a practical application, though. Because you aren't listening, the widget doesn't need to re-render just because a change was made. In this example, there would be no reason to re-render the FloatingActionButton, even when the ChangeNotifier did notify it's listeners.
Finally, just so you believe me, let's look at this running in a Flutter app:
- previous: MultiProvider micro lesson
- next: ProxyProvider