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
- add freezed_annotation to your
dependencies
- add freezed and build_runner to your
dev_dependencies
- 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;
}
- run this command in terminal
flutter pub run build_runner build --delete-conflicting-outputs
- freezed will generate your
ComplexState
as well as some other useful methods likecopyWith()
which now you can use in yourStateNotifier
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