英文:
Flutter: Riverpod StateNotifier does not notifies about changed state
问题
我成功地使用StateNotifier
与基本数据类型和使用生成器的非基本数据类型。但是,对于手动编写的非基本数据类型,它不起作用。我可以在调试器中看到setState
方法被调用,但ConsumerWidget
不反映任何更改。
以下是我的代码:
notifications_settings_provider.dart
class NotificationsSettingsStateNotifier
extends StateNotifier<NotificationsSettings> {
NotificationsSettingsStateNotifier()
: 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);
我认为这可能与StateNotifier在内部比较状态对象的方式有关,因此我已经重写了==
和hashCode
方法。但没有帮助。
目前,我认为也许我的NotificationsSettings
对象需要一个copyWith
方法,但还不确定。
我知道我可以使用代码生成器,但这个问题仍然存在,即使纯粹是学术性质的问题。
英文:
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.
答案1
得分: 1
尝试使用 freezed 或 equatable 包重写你的 NotificationsSettings
类。
更简单的方法是将 NotificationsSettings
类的所有字段实现为 final
。
英文:
Try rewriting your NotificationsSettings
class using the freezed or equatable package.
A simpler way would be to implement all fields of the NotificationsSettings
class as final
.
答案2
得分: 0
因此,感谢Ruble的答案揭示了问题,即类属性未定义为“final”。
现在,该类如下所示:
class NotificationsSettings {
final bool? enabled;
final int? timeoutInDays;
final int? delayInHours;
final String? startTime;
NotificationsSettings(
{this.enabled, this.timeoutInDays, this.delayInHours, this.startTime});
static NotificationsSettings defaultValues() {
return NotificationsSettings(
enabled: true, timeoutInDays: 1, delayInHours: 1, startTime: '09:00 AM');
}
NotificationsSettings fromJson(Map<String, dynamic> json) {
return NotificationsSettings(
enabled: json['enabled'] ?? this.enabled,
timeoutInDays: json['timeoutInDays'] ?? this.timeoutInDays,
delayInHours: json['delayInHours'] ?? this.delayInHours,
startTime: json['startTime'] ?? this.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);
NotificationsSettings copyWith(
{bool? enabled,
int? timeoutInDays,
int? delayInHours,
String? startTime}) =>
NotificationsSettings(
enabled: enabled ?? this.enabled,
timeoutInDays: timeoutInDays ?? this.timeoutInDays,
delayInHours: delayInHours ?? this.delayInHours,
startTime: startTime ?? this.startTime,
);
}
我还添加了copyWith
方法,否则无法更改状态。
顺便说一下,有一个在线的JSON转Dart转换器,可以生成具有final字段和copyWith方法的Dart类。
英文:
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:
class NotificationsSettings {
final bool? enabled;
final int? timeoutInDays;
final int? delayInHours;
final String? startTime;
NotificationsSettings(
{this.enabled, this.timeoutInDays, this.delayInHours, this.startTime});
static NotificationsSettings defaultValues() {
return NotificationsSettings(
enabled: true, timeoutInDays: 1, delayInHours:1, startTime: '09:00 AM');
}
NotificationsSettings fromJson(Map<String, dynamic> json) {
return NotificationsSettings(
enabled: json['enabled'] ?? this.enabled,
timeoutInDays: json['timeoutInDays'] ?? this.timeoutInDays,
delayInHours: json['delayInHours'] ?? this.delayInHours,
startTime: json['startTime'] ?? this.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);
NotificationsSettings copyWith(
{bool? enabled,
int? timeoutInDays,
int? delayInHours,
String? startTime}) =>
NotificationsSettings(
enabled: enabled ?? this.enabled,
timeoutInDays: timeoutInDays ?? this.timeoutInDays,
delayInHours: delayInHours ?? this.delayInHours,
startTime: startTime ?? this.startTime,
);
}
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论