ValueNotifier, but outside Flutter and with some extra perks

Overview

pub package Welcome to state_notifier~

This package is a recommended solution for managing state when using Provider or Riverpod.

Long story short, instead of extending ChangeNotifier, extend StateNotifier:

class City {
  City({required this.name, required this.population});
  final String name;
  final int population;
}


class CityNotifier extends StateNotifier<List<City>> {
  CityNotifier() : super(const <City>[]);

  void addCity(City newCity) {
    state = [
      ...state,
      newCity,
    ];
  }
}

Motivation

The purpose of StateNotifier is to be a simple solution to control state in an immutable manner.
While ChangeNotifier is simple, through its mutable nature, it can be harder to maintain as it grows larger.

By using immutable state, it becomes a lot simpler to:

  • compare previous and new state
  • implement an undo-redo mechanism
  • debug the application state

Good practices

DON'T update the state of a StateNotifier outside the notifier

While you could technically write:

class Counter extends StateNotifier<int> {
  Counter(): super(0);
}

final notifier = Counter();
notifier.state++;

That is considered an anti-pattern (and your IDE should show a warning).

Only the StateNotifier should modify its state. Instead, prefer using a method:

class Counter extends StateNotifier<int> {
  Counter(): super(0);

  void increment() => state++:
}

final notifier = Counter();
notifier.increment();

The goal is to centralize all the logic that modifies a StateNotifier within the StateNotifier itself.

FAQ

Why are listeners called when the new state is == to the previous state?

You may realize that a StateNotifier does not use == to verify that the state has changed before notifying for changes.

This behavior is voluntary, for performance reasons.

The reasoning is that StateNotifier is typically used with complex objects, which often override == to perform a deep comparison.
But performing a deep comparison can be a costly operation, especially since it is common for the state to contain lists/maps.
Similarly, for complex states, it is rare that when calling notifier.state = newState, the new and previous states are the same.

As such, instead of using ==, StateNotifier relies on identical to compare objects.
This way, when using StateNotifier with simple states like int/enums, it will correctly filter identical states. At the same time, this preserves performance on complex states, as identical will not perform a deep object comparison.

Using custom notification filter logic

You can override the method updateShouldNotify(T old,T current) of a StateNotifier to change the default behaviour, such as for:

  • using == instead of identical to filter updates, for deep state comparison
  • always returning true to revert to older behaviors of StateNotifier
  @override
  bool updateShouldNotify(User old, User current) {
    /// only update the User content changes, even if using a different instance
    return old.name != current.name && old.age != current.age;
  }

Usage

Integration with Freezed

While entirely optional, it is recommended to use StateNotifier in combination with Freezed.
Freezed is a code-generation package for data-classes in Dart, which automatically generates methods like copyWith and adds support for union-types.

A typical example would be using Freezed to handle data vs error vs loading states. With its union-types, it can lead to a significant improvement in maintainability as it:

  • ensures that your application will not enter illogical states (such as both having a "data" and being in the "loading" state)
  • ensures that logic handles all possible cases. Such as forcing that the loading/error cases be checked before trying to access the data.

The idea is that, rather than defining the data, error and loading state in a single object like:

class MyState {
  MyState(...);
  final Data data;
  final Object? error;
  final bool loading;
}

We can use Freezed to define it as:

@freezed
class MyState {
  factory MyState.data(Data data) = MyStateData;
  factory MyState.error(Object? error) = MyStateError;
  factory MyState.loading() = MyStateLoading;
}

That voluntarily prevents us from doing:

void main() {
  MyState state;
  print(state.data);
}

Instead, we can use the generated map method to handle the various cases:

void main() {
  MyState state;
  state.when(
    data: (state) => print(state.data),
    loading: (state) => print('loading'),
    error: (state) => print('Error: ${state.error}'),
  );
}

Integration with provider/service locators

StateNotifier is easily compatible with provider through an extra mixin: LocatorMixin.

Consider a typical StateNotifier:

class Count {
  Count(this.count);
  final int count;
}

class Counter extends StateNotifier<Count> {
  Counter(): super(Count(0));

  void increment() {
    state = Count(state.count + 1);
  }
}

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<Count> with LocatorMixin {
// unchanged
}

That 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<Count> with LocatorMixin {
  Counter(): super(Count(0))

  void increment() {
    state = Count(state.count + 1);
    read<LocalStorage>().writeInt('count', state.count);
  }
}

Where Counter and LocalStorage are defined using provider this way:

void main() {
  runApp(
    MultiProvider(
      providers: [
        Provider(create: (_) => LocalStorage()),
        StateNotifierProvider<Counter, Count>(create: (_) => Counter()),
      ],
      child: MyApp(),
    ),
  );
}

Then, Counter/Count are consumed using your typical context.watch/Consumer/context.select/...:

@override
Widget build(BuildContext context) {
  int count = context.watch<Count>().count;

  return Scaffold(
    body: Text('$count'),
    floatingActionButton: FloatingActionButton(
      onPressed: () => context.read<Counter>().increment(),
      child: Icon(Icons.add),
    ),
  );
}

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));
});

Note: LocatorMixin only works on StateNotifier. If you try to use it on other classes by using with LocatorMixin, it will not work.

Sponsors

Comments
  • Allow StateNotifierProvider builder context to access the newly created StateNotifierProvider

    Allow StateNotifierProvider builder context to access the newly created StateNotifierProvider

    I'm providing a state notifier that will only be used on a specific page. It would be cool to do something like this change on Provider dev release.

    https://pub.dev/packages/provider/versions/4.1.0-dev+3#-changelog-tab-

    Added builder on the different providers. This parameters simplifies situations where we need a BuildContext that can access the new provider.
    
    As such, instead of:
    
    Provider(
      create: (_) => Something(),
      child: Builder(
        builder: (context) {
          final name = context.select((Something s) => s.name);
          return Text(name);
        },
      ),
    )
    we can write:
    
    Provider(
      create: (_) => Something(),
      builder: (context, child) {
        final name = context.select((Something s) => s.name);
        return Text(name);
      },
    )
    The behavior is the same. This is only a small syntax sugar.
    
    opened by biklas7 9
  • State changes on page load

    State changes on page load

    Describe what scenario you think is uncovered by the existing examples/articles I'd like to use a StateNotifier on page load (not on any specific user interaction on the page itself). Currently, I am using initState on the StateNotifier, which fetches data from a remote server. The issue I am facing is that this is only executed once, and not each time I am navigating to the page.

    Describe why existing examples/articles do not cover this case I am assuming the above is the intended behaviour (i.e. initState is only executed once, when creating the StateNotifier). If that is the case, what is the recommended implementation for my use case described above?

    documentation 
    opened by sderungs99 8
  • Test failures swallowed in addListener callback

    Test failures swallowed in addListener callback

    I am testing stuff inside of the addListener callback, but since errors are caught and "redirected" to onError, test failures are never exposed:

        notifier.addListener(
          expectAsync1((model) {
            if (i == 0) expect(model, matcher);
            if (i == 1) expect(model, matcher);
            i++;
          }, count: 2),
          fireImmediately: false,
        );
    

    Any of those expects failing, and the test will still pass.

    I tried different ways of overriding the callback but none report back the failure:

        notifier.onError = (err, stack) {
          throw err;
          // print(err);
          // throw TestFailure(err.toString());
        };
    

    So my first workaround is checking on the counter at the end of the test:

    expect(i, equals(2));
    

    Any better ideas or workarounds?

    enhancement 
    opened by frank06 8
  • Unclear on the correct approach for updated multiple values of state object (with freezed data class)

    Unclear on the correct approach for updated multiple values of state object (with freezed data class)

    When using StateNotifier with a freezed state class, there is a need to add a setter for each state attribute that should be modifiable since the notifier's state is protected. This is cumbersome when there are many state attributes, but also it feels wrong adding what is essentially a proxy method for mutating the state on a per attribute basis.

    @freezed
    abstract class MyData implements _$MyData {
      const factory MyData(String foo, String bar) = _MyData;
    }
    
    class MyDataNotifier extends StateNotifier<MyData> {
      MyDataNotifier() : super(null);
    
      set foo(String foo) => state = state.copyWith(foo: foo);
    
      set bar(String bar) => state = state.copyWith(bar: bar);
    }
    

    This also means that modifying multiple state properties requires multiple passes of the copyWith, which seems inefficient โ€“ though perhaps is immaterial.

    final myDataNotifier = MyDataNotifier(foo: 'abc', bar: 'xyz');
    
    myDataNotifier.foo = '123'; // creates a new state object
    myDataNotifier.bar = '456'; // creates a new state object again
    

    One way around this, although seemingly against the philosophy of StateNotifier, is to expose a method that accepts a function that returns an updated state given the current state:

    class MyDataNotifier extends StateNotifier<MyData> {
      MyDataNotifier() : super(null);
    
      // Allow consumer to update state without needing to have the state first
      void update(MyData Function (MyData) updater) => state = updater(state);
    }
    
    final myDataNotifier = MyDataNotifier(foo: 'abc', bar: 'xyz');
    
    myDataNotifier.update((state) => state.copyWith(foo = '123', bar = '456'));
    

    I'm wondering whether there is any guidance on the best way to be using StateNotifier, particularly with freezed, when multiple attributes need to be updated in one section of code.

    enhancement 
    opened by scopendo 7
  • Unhandled Exception: Instance of 'DependencyNotFoundException<PrefsSupport>' when read<>() from StateNotifier

    Unhandled Exception: Instance of 'DependencyNotFoundException' when read<>() from StateNotifier

    void main:

    void main() => runApp(MultiProvider(providers: [
          Provider<PrefsSupport>(create: (_) => PrefsSupport()),
          StateNotifierProvider<MainNotifier, MainState>(
            create: (_) => MainNotifier(),
          )
        ], child: MyApp()));
    

    my PrefsSupport:

    class PrefsSupport {
      PrefsSupport() {
        _init();
      }
    
      static const String store_key = 'make_sleep_better';
      static const String delay_minute_key = 'delay_minute';
      static const String dark_mode = 'dark_mode';
      Future<SharedPreferences> _prefs;
    
      void _init() {
        _prefs = SharedPreferences.getInstance();
      }
    
      Future<bool> saveDarkMode(bool value) async {
        return (await _prefs).setBool(dark_mode, value);
      }
    
      Future<bool> getDarkMode() async {
        await Future.delayed(const Duration(seconds: 1));
        return (await _prefs).getBool(dark_mode) ?? false;
      }
    
      Future<bool> saveDelayMinute(int delayMinute) async {
        return (await _prefs).setInt(delay_minute_key, delayMinute);
      }
    
      Future<int> getDelayMinute() async {
        return (await _prefs).getInt(delay_minute_key) ?? 14;
      }
    }
    

    my state_notifier class

    class MainNotifier extends StateNotifier<MainState> with LocatorMixin {
      MainNotifier() : super(const MainState()) {
        _fileStore = const FileStore();
        _prefsSupport = PrefsSupport();
        _initLoad();
      }
    
      PrefsSupport _prefsSupport;
      FileStore _fileStore;
    
      void _initLoad() async {
        var test = await read<PrefsSupport>().getDarkMode(); // error in this line
    
        print('test is $test');
        final darkMode = await _prefsSupport.getDarkMode();
        state = MainState(darkMode: darkMode);
      }
    ....
    
    opened by tbm98 7
  • StateNotifierProvider.value(... does not trigger state changes

    StateNotifierProvider.value(... does not trigger state changes

    I can't seem to get the StateNotifierProvider to work when supplying an existing StateNotifier. The following modified example will not trigger any changes to the app.

    void main() {
      var notifier = MyStateNotifier();
    
      runApp(
        MultiProvider(
          providers: [
            StateNotifierProvider<MyStateNotifier, MyState>.value(
              value: notifier
            )
          ],
          child: MyApp(),
        ),
      );
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          title: 'Flutter Demo',
          home: MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatelessWidget {
      const MyHomePage({Key key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Counter example'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text(
                  'You have pushed the button this many times:',
                ),
                Text(
                  context.select((MyState value) => value.count).toString(),
                  style: Theme.of(context).textTheme.title,
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: context.watch<MyStateNotifier>().increment,
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        );
      }
    }
    
    question 
    opened by jamiewest 7
  • A few state notifiers stopped working, is it due to updateShouldNotify?

    A few state notifiers stopped working, is it due to updateShouldNotify?

    I just did a flutter pub upgrade a few hours ago and the only thing I noticed changed was this package.

    At the same time a few (not all) of my state notifiers stopped working as expected and reading through I just noticed this was changed https://github.com/rrousselGit/state_notifier/pull/57

    Wondering if this is related or if there is any quick statement on how this updateShouldNotify works?

    My code that stopped working is:

    final paginationProvider = StateProvider.family((ref, id) => true);
    
      return Container(
              child: Consumer(
              builder: (context, watch, _)
                    {
    
                      _showPaginationBar = ref.watch(paginationProvider(widget.randomHash).state).state;
    

    This provider is updated in another class, when the screen is scrolled as follows:

        if ((_thisScrollDirection == 'down') && (metrics.pixels > _lastTappedScrollPosition+100.0))
        {
          ref.read(paginationProvider(_randomHash).state).state = false;
        }
    

    It doesn't seem to update anymore, maybe I don't need Consumer anymore since it is inside a ConsumerStatefulWidget ? This is some of my most early/old code in my app, but I am unsure if this is the reason?

    It all seemed to be working smoothly just before this update

    Thanks!

    bug 
    opened by mark8044 6
  • Mocking StateNotifier

    Mocking StateNotifier

    Hey,

    I want to mock a StateNotifier so I can control what states are emitted.

    For this I created:

    class MockStateNotifier<T> extends StateNotifier<T> with Mock {
      MockStateNotifier(T state) : super(state);
    }
    

    to mock all custom methods declared but to retain the listening logic.

    In a test I can do:

    class MockRegistrationStateNotifier extends MockStateNotifier<RegistrationState>
        implements RegistrationStateNotifier {
      MockRegistrationStateNotifier() : super(RegistrationState.unauthenticated());
    }
    
    [...]
    
        mockRegistrationStateNotifier.state = RegistrationState.error(emailError: 'error');
        await tester.pump();
        expect(find.text('error'), findsOneWidget);
    
    

    This works perfectly fine, the only issue I have with this is that the linter complains as accessing state is protected. Unfortunately I cannot use debugState here as it only exposes a getter.

    Is there another way to do this? If not, could 'debugState' also expose a way of setting the state?

    Thanks in advance!

    documentation 
    opened by Norbert515 6
  • Implement ChangeNotifier vs StateNotifer

    Implement ChangeNotifier vs StateNotifer

    I familiar with Provider package and combine it with ChangeNotifier . I have 3 getter and method with different function :

    1. Toggle Loading
    2. Toggle Image Loading
    3. Toggle ObsecurePassword

    Using ChangeNotifer

    import 'package:flutter/foundation.dart';
    
    class GlobalChangeNotifier extends ChangeNotifier {
      bool _isLoading = false;
      bool _isImageLoading = false;
      bool _isObsecurePassword = false;
    
      bool get isLoading => _isLoading;
      bool get isImageLoading => _isImageLoading;
      bool get isObsecurePassword => _isObsecurePassword;
    
      void setLoading(bool value) {
        _isLoading = value;
        notifyListeners();
      }
    
      void setImageLoading(bool value) {
        _isImageLoading = value;
        notifyListeners();
      }
    
      void setObsecurePassword(bool value) {
        _isObsecurePassword = !value;
        notifyListeners();
      }
    }
    
    final globalChangeNotifier = GlobalChangeNotifier();
    

    If i use ChangeNotifier , i only need create 1 file and just call method like globalChangeNotifier.METHOD() or value like globalChangeNotifier.value.

    But now i learn about Riverpod package , and in the documentation used StateNotifier. I want migrate my above code from ChangeNotifier to StateNotifier , but in my understanding StateNotifier only can hold 1 type data, so if i want migrate above code i should create 3 file , let's say provider_isloading.dart,provider_isimageloading.dart,provider_obsecurepassword.dart.

    Using StateNotifier

    // provider_isloading.dart
    class IsImageLoading extends StateNotifier<bool> {
      IsImageLoading() : super(false);
    
      void toggleImageLoading(bool value) {
        state = value;
      }
    }
    
    final isImageLoadingProvider = StateNotifierProvider((ref) => IsImageLoading());
    
    // provider_isimageloading.dart
    
    class IsLoading extends StateNotifier<bool> {
      IsLoading() : super(false);
      void toggleLoading(bool value) => state = value;
    }
    
    final isLoadingProvider = StateNotifierProvider((ref) => IsLoading());
    
    // provider_obsecurepassword.dart
    class IsObsecurePassword extends StateNotifier<bool> {
      IsObsecurePassword() : super(false);
    
      void toggleObsecurePassword(bool value) {
        state = !value;
      }
    }
    
    final isObsecurePasswordProvider = StateNotifierProvider((ref) => IsObsecurePassword());
    
    

    And i create 1 file to export all those file:

    GlobalStateNotifier.dart

    export './provider_loading.dart';
    export './provider_imageloading.dart';
    export './provider_obsecurepassword.dart';
    

    My question is , it's best practice make it like this ?

    Structure Folder

    tanya statenotifier
    opened by zgramming 6
  • [Question] Does StateNotifier works with StreamProvider

    [Question] Does StateNotifier works with StreamProvider

    Does state_notifier support StreamProvider for watching changes so, for example, I'm trying to listen to connectivity changes.

    Here's the example:

    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MultiProvider(
          providers: [
            StreamProvider<ConnectivityResult>(
              create: (_) => Connectivity().onConnectivityChanged,
            ),
            ChangeNotifierProvider<Counter>(
              create: (_) => Counter(),
            ),
          ],
          child: MaterialApp(
            home: MyHomePage(),
          ),
        );
      }
    }
    
    class Counter extends ValueNotifier<int> with LocatorMixin {
      Counter() : super(0);
    
      void increment() {
        this.value++;
        if (read<ConnectivityResult>() != ConnectivityResult.none) {
          // call api
        }
      }
    }
    
    class MyHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('My Home Page'),
          ),
          body: Center(
            child:
                Text('You clicked button ${context.watch<Counter>().value} times'),
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () => context.read<Counter>().increment(),
          ),
        );
      }
    }
    
    opened by hsul4n 6
  • Need better docs

    Need better docs

    As far as I can see, State Notifier is a great and underestimated package that solves two separate problems:

    1. Setup Store ((that's how I call a business-logic class provided by Provider) to be state-based (as in Redux or BLoC), that allows me to move all the data to states and access the current state from UI. Thereby, I can use all the benefits of immutable states (for example, creating an undo/redo with state history), but without complexity of patterns mentioned above.

    2. Locate services. Say, if I have a custom configured Dio-HttpClient, before I should have made it a singleton in order to access it from Store. Because I do not have a context there. Now I can use read / update methods to directly get this Dio client instance by its Type.

    Needs more explanation. Seems like there's no Selector or context.select() for StateNotifier? What if I'm getting the same state with small data changes, and I want to subscribe only to those changes selectively, not the whole state updates? Can I have a mixed Store that both extends StateNotifier and with ChangeNotifier? So it has one part of its data in states, and another part inside the store? (sorry, if it's a silly question)

    opened by subzero911 6
  • StateNotifierProvider.value doesn't handle LocatorMixin

    StateNotifierProvider.value doesn't handle LocatorMixin

    Describe the bug By using this:

    StateNotifierProvider<BalanceStore, BalanceState>(create: (_) => BalanceStore()),
    

    I can use read methods of locatorMixin.

    But using:

    StateNotifierProvider<BalanceStore, BalanceState>.value(balanceStore),
    

    Then read crash with not found dependency.

    By reading the source look like one is setting (value as LocatorMixin)..read = _contextToLocator(context) but it is not done on the other case.

    bug 
    opened by jaumard 0
  • StateListenable

    StateListenable

    Is your feature request related to a problem? Please describe. I have some classes in our app that I pass in a StateNotifier as a dependency. For example:

    class RedirectService({required StateNotifier<AuthState> authStateNotifier}) {
    

    The problem is that, this class could accidentally modify this value by calling .state = value. The intent is also unclear because it appears that the RedirectService has read-write access. If another developer is reading the code, they might think it's OK to set the state.

    Describe the solution you'd like An abstract interface called StateListenable<T> that is similar to ValueListenable. It would have all of the listen/read methods of StateNotifier except it wouldn't include the methods to set state.

    A StateNotifier<T> would implement StateListenable<T>

    Then my constructor would change to this:

    class RedirectService({required StateListenable<AuthState> authStateListenable}) {
    

    Describe alternatives you've considered I've considered subclassing StateNotifier and raising an exception if the value is modified, but that feels messy and the intent is unclear. You don't know at compile time that this is unexpected behavior.

    I've also considered using ValueListenable, but then I like that state_notifier is not coupled to Flutter.

    enhancement 
    opened by venkatd 0
  • Add StateNotifierListener for create common widgets with no boilerplate code, no matter if provider or riverpod is used

    Add StateNotifierListener for create common widgets with no boilerplate code, no matter if provider or riverpod is used

    It is useful to have a widget that is similar to bloc's BlocListener , to avoid boilerplate and dependencies on provider or riverpod.

    Usually you use listen or watch or Consumer respectively from riverpod or provider.

    But in some projects of my work we use provider and in others we are using riverpod, but we have some common StateNotifierss.

    So in order to share widgets between projects regardless of whether we use provider or riverpod, we use a StateNotifierBuilder instead of watch or Consumer, but some widgets has some listeners with predefined behaviors, so we use a widget similar to this , that is a StateNotifierListener

    Having the StateNotifierListener widget as part of flutter_state_notifier would be ideal, so you avoid having to create it yourself or use external packages.

    enhancement 
    opened by MiniSuperDev 0
  • Automatic persistence?

    Automatic persistence?

    Is your feature request related to a problem? Please describe. I use a StateNotifier for state management (with riverpod) and I got it to work. However, now I would like to persist the state so that it survives application restarts.

    Describe the solution you'd like Libraries that I have used before, like redux or bloc, both support automatic persistence, normally as separate packages (redux_persist and hydrated_bloc). I have looked around and it seems that there is nothing out of the box for state_notifier. I understand it is not hard to do myself, but I expect a lot of people to have to write very similar code to solve this. Would it be possible to also have some kind of automatic persistence?

    For serialization on state changes, I think the easiest way that would work for all StateNotifiers would be to do it in a listener - whenever it is fired, it schedules a write. I suppose this could be a separate package like state_notifier_persistence, which offers some abstraction layer (like an abstract Storage interface, with a possible default implementation with hive (it seems to be the go-to library for things like this in Flutter), possibly in yet another package, like state_notifier_persistence_hive. state_notifier_persistence would also provide the said listener (PersistingListener?) which takes an instance of Storage and uses it.

    For deserialization (i.e. when a StateNotifier is created and there already exist state in storage) I don't have a nice API in mind yet. One option would be to have a mixin that provides storage and overrides the state getter and if the state is null, it uses storage to read it from disk. I beliveve this is what hydrated_bloc does.

    Describe alternatives you've considered I've implemented it myself for my application (very dirty, very ugly for now, no abstraction over hive etc., it is just a PoC). As far as I know, there are no alternatives to this, except migrating away to other state-management libraries.

    enhancement 
    opened by wujek-srujek 10
  • Removing a listener inside itself causes an exception

    Removing a listener inside itself causes an exception

    It causes an exception to remove a listener inside itself using the function returned from addListener() of StateNotifier.

    To Reproduce

    Run the code below and see the output.

    class Counter extends StateNotifier<int> {
      Counter() : super(0);
    
      void increment() => state++;
    }
    
    Function _removeCounterListener;
    Timer _timer;
    
    void main() {
      final counter = Counter();
      _removeCounterListener = counter.addListener(_counterListener);
    
      _timer = Timer.periodic(const Duration(seconds: 1), (_) {
        counter.increment();
      });
    }
    
    void _counterListener(int state) {
      print('Counter: $state');
    
      if (state == 3) {
        _removeCounterListener();
        _timer.cancel();
      }
    }
    

    Output:

    Counter: 0
    Counter: 1
    Counter: 2
    Counter: 3
    Unhandled exception:
    Concurrent modification during iteration: Instance of '_LinkedListIterator<_ListenerEntry<int>>'.
    #0      _LinkedListIterator.moveNext (dart:collection/linked_list.dart:191:7)
    #1      StateNotifier.state= (package:state_notifier/state_notifier.dart:160:33)
    #2      Counter.increment (file:///D:/state_notifier_issue/main.dart:29:23)
    #3      main.<anonymous closure> (file:///D:/state_notifier_issue/main.dart:13:13)
    #4      _Timer._runTimers (dart:isolate-patch/timer_impl.dart:397:19)
    #5      _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:428:5)
    #6      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)
    

    Expected behavior

    No exception occurs.

    enhancement 
    opened by kaboc 2
Owner
Remi Rousselet
Flutter enthusiast. You'll find me on stackoverflow. Or as a speaker in Flutter meetups
Remi Rousselet
LakhanKumawat แต–โบ 12 Dec 6, 2022
Music player application for android. It's uses MVVM architecture and Provider & ValueNotifier state management.

music-player-flutter Flutter music player application which is my personal project published to play store. Project structures are as following,

null 30 Jul 10, 2022
Parasite - Exploring ChangeNotifier and ValueNotifier

Parasite An ultra lightweight state management utility highly inspired by Provid

Boris Kayi 1 Jan 14, 2022
Value listenable test - Assists in testing ValueListenable objects (ex: ValueNotifier).

value_listenable_test Assists in testing ValueListenable objects (ex: ValueNotifier). install Added in your pubspec.yaml as dev dependency: dev_depend

Flutterando 3 Feb 23, 2022
A fast, extra light and synchronous key-value storage to Get framework

get_storage A fast, extra light and synchronous key-value in memory, which backs up data to disk at each operation. It is written entirely in Dart and

Jonny Borges 257 Dec 21, 2022
A Funtioning basic Clock UI APP with extra functionalities such as displaying thecurrent time location being used and checking time for other timezones simultaneosly.

clock_UI A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if this

Anjola Favour Ayomikun 0 Dec 28, 2021
Add an extra tap region to button widgets

ExtraTapRegion A widget to add an extra tap region around the child widget. Example @override Widget build(BuildContext context) { return DeferredPo

Yusuke Otsuka 0 May 17, 2022
Hooks but for and of Flutter

reactives A new way for Flutter to reuse/group common logic. Think of them like React hooks but for and of flutter. Idea copied from the lit world Mot

Ripe Mango 27 Dec 22, 2022
An opinionated, community-driven set of lint rules for Dart and Flutter projects. Like pedantic but stricter

Lint for Dart/Flutter lint is a hand-picked, open-source, community-driven collection of lint rules for Dart and Flutter projects. The set of rules fo

Pascal Welsch 257 Jan 3, 2023
A Simple but elegant Calculator app made with Flutter using Google's Material Design with Currency (Exchange Rate) and Unit Converter.

Calculator A Simple but elegant Calculator app made with Flutter using Google's Material Design with Currency (Exchange Rate) and Unit Converter. Down

Hemant Rajput 15 Dec 7, 2022
Automatically generate profile picture with random first name and background color. But you can still provide pictures if you have them. As the default color, based on the name of the first letter. :fire: :fire: :fire:

FLUTTER PROFILE PICTURE Automatically generate profile picture with random first name and background color. But you can still provide pictures if you

Aditya Dharmawan Saputra 10 Dec 20, 2022
Advanced & beautiful animations inspired by animate_do and Animate.css, every animation is a widget that contains default but customizable values ๐Ÿ’™

animate_it An animation package inspired by animate_do and Animate.css. Since the original animate_do is not being maintained, I have decided to creat

Luke Moody 3 Oct 1, 2022
Album Image is based in photo_manager package and has the same concept as image_picker but with a more attractive interface to choose an image or video from the device gallery, whether it is Android or iOS

Album Image is based in photo_manager package and has the same concept as image_picker but with a more attractive interface to choose an image or vide

Phuong Vu 2 Oct 13, 2022
This is an open source Tips & Tricks for Flutter, that helps flutter Developers write simple but powerful dart codes.

Showcasing-flutter - This is an open source Tips & Tricks for Flutter, that helps flutter Developers write simple but powerful dart codes.

Paul Edeme'kong - Flutter Fairy 1 Jan 4, 2022
Simple but pretty cool birthday countdown app built using flutter

Simple but pretty cool birthday countdown app "For You" ?? Don't forget to star โญ the repo if you like what you I have created ?? . ?? GIF of a sample

Ruslan Hasanov 6 Aug 13, 2022
A Flutter based to do list app (yes, another to do list... but... this time based on a beautiful design)

โœ”๏ธ Flutter to do App "To Do List" A Flutter app based on the design of the To Do App, created by Rudi Hartono, see more on: Uplabs. Getting Started ??

Johan 561 Dec 31, 2022
BubbleShowcase is a small but power flutter package that allows you to highlight specific parts of your app to explain them to the user or to showcase your app new features.

BubbleShowcase BubbleShowcase is a small but powerful flutter package that allows you to highlight specific parts of your app (to explain them to the

Hugo Delaunay 38 Oct 26, 2022
The same old Weather App, But 'In Flutter , By Me'

Weather App v1.0.1 About The same old Weather App, But 'In Flutter , By Me' . NB Still in developement Stats ### v1.0.1 - Started a Basic outline of

Adithya Krishnan 2 Sep 7, 2022
An easy-to-use flutter http network requests handler with more functionality than http but more simpler than dio.

network_requests An easy-to-use flutter http network requests handler with more functionality than http but more simpler than dio. Platform Supported

Coder_Manuel 3 Dec 15, 2022