Home > database >  How to handle complex state with Riverpods StateNotifierProvider
How to handle complex state with Riverpods StateNotifierProvider

Time:06-04

I have a very big, complex app with lots of pages with input forms with lots of fields (texts, date-selection, combo-boxes,...), so the state is rather big. I'm switching from Provider to Riverpod, and the proposed way for state management seems to be primarily StateNotifierProvider. But that state is immutable, meaning you have to clone the complete state on every change. What I would need is Providers ChangeNotifierProvider (that Riverpod has too), in order to be able to have mutable state and more control over when rebuilds on the listeners are triggered. But Riverpod discourages the use of ChangeNotifierProvider, what makes me a little nervous. Maybe I didn't really understand the StateNotifierProvider concept.

So the question is, how can I use StateNotifierProvider to handle complex state? Is it really necessary to always clone the state?

Edit: With complex I mean a larger number of fields (e.g. 50) and also lists and object-structures. You could clone that, but it's expensive and it doesn't feel natural/smart to do this on every little change. Is this really the intended way? Is there a way around cloning while still using StateNotifierProvider?

CodePudding user response:

First of all i wouldn't recommend using ChangeNotifier(mutable state) over StateNotifier(immutable state), as best practical way of state management in flutter is to use immutable state.

You can use freezed and freezed_annotation to help you copy the state each time you want to emit new one, you just copy the old state and change what you need. Something like this

state = state.copyWith(var1: "new var");

here is an example of complex state using freezed

  1. add freezed_annotation to your dependencies
  2. add freezed and build_runner to your dev_dependencies
  3. create your state complex_state.dart something like this, change the factory constructor to match your needs
import 'package:freezed_annotation/freezed_annotation.dart';

part 'complex_state.freezed.dart';

@freezed
class ComplexState with _$ComplexState {
  const ComplexState._();

  const factory ComplexState({
    required String var1,
    required double var2,
    required bool var3,
    required int var4,
  }) = _ComplexState;
}
  1. run this command in terminal
flutter pub run build_runner build --delete-conflicting-outputs
  1. freezed will generate your ComplexState as well as some other useful methods like copyWith() which now you can use in your StateNotifier

Here is an example of using StateNotifier with Riverpod

final complexStateNotifierProvider = StateNotifierProvider(
  (ref) {
    const initialState = ComplexState(
      var1: "var1",
      var2: 90.0,
      var3: true,
      var4: 90,
    );
    return ComplexStateNotifier(initialState);
  },
);

abstract class ComplexStateEvent {}

class ComplexStateEventDoSomething implements ComplexStateEvent {}

class ComplexStateNotifier extends StateNotifier<ComplexState> {
  ComplexStateNotifier(ComplexState state) : super(state);

  void onEvent(ComplexStateEvent event) {
    if (event is ComplexStateEventDoSomething) {
      state = state.copyWith(var1: "new value");
    }
  }
}

Note: freezed also support nullable and default constructor parameters which you may use for the initial state, read more on its pub.dev page

  • Related