Using Riverpod with Objects - always reinstantiate once property changes?

433

To trigger a state change you have to use the state setter. The implementation looks like this:

  @protected
  set state(T value) {
    assert(_debugIsMounted(), '');
    final previousState = _state;
    _state = value;

    /// only notify listeners when should
    if (!updateShouldNotify(previousState, value)) {
      return;
    }

    _controller?.add(value);
    // ...

The internal StreamController<T> _controller needs to be triggered (add), to notify listeners (in this case riverpod) about updates.

By using state.name = something you're not informing the StateNotifier about a new state (not calling the state setter). Only your object holds the new value but nobody was notified.

Your state is mutable and that very often leads to such misbehavior. By using an immutable object you can prevent such errors in the first place. Write it yourself or use freezed.

Learn more about immutability in my talk

Share:
433
Fellow7000
Author by

Fellow7000

Updated on December 19, 2022

Comments

  • Fellow7000
    Fellow7000 over 1 year

    I am making first steps with Riverpod and just want to check if my understanding of handling changes of some data class properties using Riverpod is correct.

    Imagine, I have a data class like that:

     class MyClass {
      final String name;
      final int id;
    
      const MyClass(this.name, this.id);
     }
    

    Then I create a StateNotifier:

    class MyClassStateNotifier extends StateNotifier<MyClass> {
      MyClassStateNotifier(MyClass state) : super(state);
    
      void setName(String name) {
        state.name = name;
      }
    }
    

    And this won't work - UI will not be rebuilt after calling setName this way.

    So I need to modify classes in the following way:

     class MyClass {
      final String name;
      final int id;
    
      const MyClass(this.name, this.id);
      
      MyClass copyWith({name, id}) {
        return MyClass(name ?? this.name, id ?? this.id);
      }
     }
    

    and the StateNotifier as following:

    class MyClassStateNotifier extends StateNotifier<MyClass> {
      MyClassStateNotifier(MyClass state) : super(state);
    
      void setName(String name) {
        state = state.copyWith(name: name);
      }
    }
    

    This pair will work and the UI will be rebuilt.

    So, my question: does one always need to reinstantiate the object in this way?..

    From my perspective, this is a bit strange (simple datatypes like String / int do not require this) and the boilerplate for copyWith method might become pretty huge if I have a dozen of object's properties.

    Is there any better solution available for Riverpod or is it the only one and correct?..

    Thanks in advance! :)

  • Fellow7000
    Fellow7000 about 2 years
    Danke für die Rückmeldung! :) Thanks for your answer! I've watched your video, so as I far as I understood it, this would be the only way to use Riverpod, one should focus in immutability and use plaug-ins to autogenerate the boilerplate code. Am I correct?