Skip to content

ValueNotifier, but outside Flutter and with some extra perks

License

Notifications You must be signed in to change notification settings

locker5/state_notifier

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pub package Welcome to state_notifier~

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_notifier and 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 packages 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 & notifyListeners equivalents.
  • 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.value is renamed to state, to match the class name
  • StateNotifier is abstract
  • state is @protected
  • The listener passed to addListener receives the current state, and is called synchronously on addition.
  • addListener and removeListener are fused in a single addListener function 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 mounted boolean to know if the StateNotifier was disposed or not, similar to State.

About

ValueNotifier, but outside Flutter and with some extra perks

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Dart 100.0%