I successfully used StateNotifier
with primitives and also with non-primitives using a generator. But with the manually written non-primitive type, it does not work. I can see in the debugger that the setState
method is called, but ConsumerWidget does not reflect any change.
Here is my code:
notifications_settings_provider.dart
class NotificationsSettingsStateNotifier
extends StateNotifier<NotificationsSettings> {
NotificationsSetttingsStateNotifier()
: super(NotificationsSettings.defaultValues());
setState(NotificationsSettings val) {
state = val;
}
}
final notificationsProvider = StateNotifierProvider<
NotificationsSettingsStateNotifier, NotificationsSettings>((ref) {
return NotificationsSettingsStateNotifier();
});
notifications_settings.dart
class NotificationsSettings {
bool? enabled;
int? timeoutInDays;
int? delayInHours;
String? startTime;
NotificationsSettings(
{this.enabled, this.timeoutInDays, this.delayInHours, this.startTime});
NotificationsSettings.defaultValues() {
enabled = true;
timeoutInDays = 1;
delayInHours = 3;
startTime = '09:00 AM';
}
NotificationsSettings.fromJson(Map<String, dynamic> json) {
enabled = json['enabled'];
timeoutInDays = json['timeoutInDays'];
delayInHours = json['delayInHours'];
startTime = json['startTime'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['enabled'] = this.enabled;
data['timeoutInDays'] = this.timeoutInDays;
data['delayInHours'] = this.delayInHours;
data['startTime'] = this.startTime;
return data;
}
@override
bool operator ==(Object other) =>
other is NotificationsSettings &&
other.runtimeType == runtimeType &&
other.enabled == enabled &&
other.timeoutInDays == timeoutInDays &&
other.delayInHours == delayInHours &&
other.startTime == startTime;
int get hashCode =>
Object.hash(enabled, timeoutInDays, delayInHours, startTime);
}
notifications_screen.dart
class Notifications extends ConsumerWidget {
Notifications({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
var notificationsSettings = ref.watch(notificationsProvider);
...
onToggle: (index) {
notificationsSettings.enabled = index == 0 ? true : false;
ref
.read(notificationsProvider.notifier)
.setState(notificationsSettings);
I thought that it can be somehow related to how StateNotifier internally compares state objects and I have overridden ==
and hashcode
. Did not help.
Currently, I think that maybe my NotificationsSettings
object needs copyWith
method, but does not sure yet.
I know that I can use a code generator, but the question still stands even as purely academic.
2
Answers
So, thanks to Ruble's answer revealed that the problem was that class properties were not defined as
final
.Now, the class looks as follows:
I also added
copyWith
method, as otherwise there is no way to change the state.BTW, there is online JSON to Dart converter that generates Dart classes with final fields and copyWith method.
Try rewriting your
NotificationsSettings
class using the freezed or equatable package.A simpler way would be to implement all fields of the
NotificationsSettings
class asfinal
.