I am quite new to this framework and working on state management using provider package where I come across ChangeNotifierProvider
and ChangeNotifierProvi
ValueNotifier and ChangeNotifier are closely related.
In fact, ValueNotifier is a subclass of ChangeNotifier that implements ValueListenable.
This is the implementation of ValueNotifier in the Flutter SDK:
/// A [ChangeNotifier] that holds a single value.
///
/// When [value] is replaced with something that is not equal to the old
/// value as evaluated by the equality operator ==, this class notifies its
/// listeners.
class ValueNotifier<T> extends ChangeNotifier implements ValueListenable<T> {
/// Creates a [ChangeNotifier] that wraps this value.
ValueNotifier(this._value);
/// The current value stored in this notifier.
///
/// When the value is replaced with something that is not equal to the old
/// value as evaluated by the equality operator ==, this class notifies its
/// listeners.
@override
T get value => _value;
T _value;
set value(T newValue) {
if (_value == newValue)
return;
_value = newValue;
notifyListeners();
}
@override
String toString() => '${describeIdentity(this)}($value)';
}
So, when should we use ValueNotifier vs ChangeNotifier?
Use ValueNotifier if you need widgets to rebuild when a simple value changes. Use ChangeNotifier if you want more control on when notifyListeners() is called.
Let's take this in steps.
A class that extends ChangeNotifier
can call notifyListeners()
any time data in that class has been updated and you want to let a listener know about that update. This is often done in a view model to notify the UI to rebuild the layout based on the new data.
Here is an example:
class MyChangeNotifier extends ChangeNotifier {
int _counter = 0;
int get counter => _counter;
void increment() {
_counter++;
notifyListeners();
}
}
I wrote more about this in A beginner’s guide to architecting a Flutter app.
ChangeNotifierProvider
is one of many types of providers in the Provider package. If you already have a ChangeNotifier class (like the one above), then you can use ChangeNotifierProvider
to provide it to the place you need it in the UI layout.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<MyChangeNotifier>( // define it
create: (context) => MyChangeNotifier(), // create it
child: MaterialApp(
...
child: Consumer<MyChangeNotifier>( // get it
builder: (context, myChangeNotifier, child) {
...
myChangeNotifier.increment(); // use it
Note in particular that a new instance of the MyChangeNotifier class was created in this line:
create: (context) => MyChangeNotifier(),
This is done one time when the widget is first built, and not on subsequent rebuilds.
Use ChangeNotifierProvider.value
if you have already created an instance of the ChangeNotifier
class. This type of situation might happen if you had initialized your ChangeNotifier
class in the initState()
method of your StatefulWidget
's State
class.
In that case, you wouldn't want to create a whole new instance of your ChangeNotifier
because you would be wasting any initialization work that you had already done. Using the ChangeNotifierProvider.value
constructor allows you to provide your pre-created ChangeNotifier
value.
class _MyWidgeState extends State<MyWidge> {
MyChangeNotifier myChangeNotifier;
@override
void initState() {
myChangeNotifier = MyChangeNotifier();
myChangeNotifier.doSomeInitializationWork();
super.initState();
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<MyChangeNotifier>.value(
value: myChangeNotifier, // <-- important part
child: ...
Take special note that there isn't a create
parameter here, but a value
parameter. That's where you pass in your ChangeNotifier
class instance. Again, don't try to create a new instance there.
Does the official documentation help?
DO use ChangeNotifierProvider.value to provider an existing
ChangeNotifier
:
ChangeNotifierProvider.value(
value: variable,
child: ...
)
DON'T reuse an existing
ChangeNotifier
using the default constructor.
ChangeNotifierProvider(
builder: (_) => variable,
child: ...
)
Also check out this Github issue from the author about this.
Is an important difference between ChangeNotifierProvider.value and with the create function. When you're using Provider in a single list or grid item, Flatter removes items when they leave the screen and re adds them when they reentered the screen in such situations what actually happens is that the widget itself is reused by Flutter and just the data that's attached to it changes. So Flatter recycles the same widget it doesn't destroy it and recreate it. when we are using Provider with the create function.
ChangeNotifierProvider(
create: (_) => new MyChangeNotifier(),
child: ...
)
☝☝☝ here which is content changes over time and our provider won't pick us up.
In a single list or grid item, we should use Provider dot value.
ChangeNotifierProvider.value(
value: new MyChangeNotifier(),
child: ...
)