state_notifier 0.6.0  state_notifier: ^0.6.0 copied to clipboard
state_notifier: ^0.6.0 copied to clipboard
ValueNotifier, but outside Flutter and with some extra perks
This repository is a set of packages that reimplements ValueNotifier outside of Flutter.
It is spread across two packages:
- state_notifier, a pure Dart package containing the reimplementation of ValueNotifier.
 It comes with extra utilities for combining our "ValueNotifier" with provider and to test it.
- flutter_notifier, a binding between- state_notifierand Flutter.
 It adds things like ChangeNotifierProvider from provider, but compatible with- state_notifier.
Motivation #
Extracting ValueNotifier outside of Flutter in a separate package has two purposes:
- It allows Dart packages with no dependency on Flutter to use these
classes.
 This means that we can use them on AngularDart for example.
- It allows solving some common problems with the original ChangeNotifier/ValueNotifier and/or their combination with provider.
For example, by using state_notifier instead of the original ValueNotifier, then
you get:
- A significant simplification of the integration with provider
- Simplified testing/mocking
- Improved performances on addListener¬ifyListenersequivalents.
- Extra safety through small API changes
Integration with provider/service locators #
StateNotifier is easily compatible with provider through an extra mixin: LocatorMixin.
Consider a typical StateNotifier written like a ValueNotifier:
class Counter extends StateNotifier<int> {
  Counter(): super(0)
  void increment() {
    state++;
  }
}
In this example, we may want to use Provider.of/context.read to connect our
Counter with external services.
To do so, simply mix-in LocatorMixin as such:
class Counter extends StateNotifier<int> with LocatorMixin {
// unchanged
}
This then gives you access to:
- read, a function to obtain services
- update, a new life-cycle that can be used to listen to changes on a service
We could use them to change our Counter incrementation to save the counter in
a DB when incrementing the value:
class Counter extends StateNotifier<int> {
  Counter(): super(0)
  void increment() {
    state++;
    read<LocalStorage>().writeInt('count', state);
  }
}
Testing
When using LocatorMixin, you may want to mock a dependency for your tests.
Of course, we still don't want to depend on Flutter/provider to do such a thing.
Similarly, since state is protected, tests need a simple way to read the state.
As such, LocatorMixin also adds extra utilities to help you with this scenario:
myStateNotifier.debugMockDependency<MyDependency>(myDependency);
print(myStateNotifier.debugState);
myStateNotifier.debugUpdate();
As such, if we want to test our previous Counter, we could mock LocalStorage
this way:
test('increment and saves to local storage', () {
  final mockLocalStorage = MockLocalStorage();
  final counter = Counter()
    ..debugMockDependency<LocalStorage>(mockLocalStorage);
  expect(counter.debugState, 0);
  counter.increment(); // works fine since we mocked the LocalStorage
  expect(counter.debugState, 1);
  // mockito stuff
  verify(mockLocalStorage.writeInt('int', 1));
});
Differences with ValueNotifier #
This is not a one-to-one reimplementation of ValueNotifier. It has some differences:
- ValueNotifier is instead named StateNotifier (to avoid name clash)
- ValueNotifier.valueis renamed to- state, to match the class name
- StateNotifier is abstract
- stateis- @protected
- The listener passed to addListenerreceives the currentstate, and is called synchronously on addition.
- addListenerand- removeListenerare fused in a single- addListenerfunction which returns a function to remove the listener.
 This makes adding and removing listeners O(1) versus O(N) for ValueNotifier.
- listeners cannot add extra listeners.
 This makes notifying listeners O(N) versus O(N²) for ValueNotifier
- offers a mountedboolean to know if the StateNotifier was disposed or not, similar toState.