Flutter: Riverpod StateNotifier 不会通知状态已更改。

huangapple go评论115阅读模式
英文:

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&lt;NotificationsSettings&gt; {
  NotificationsSetttingsStateNotifier()
      : super(NotificationsSettings.defaultValues());

  setState(NotificationsSettings val) {
    state = val;
  }
}

final notificationsProvider = StateNotifierProvider&lt;
    NotificationsSettingsStateNotifier, NotificationsSettings&gt;((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 = &#39;09:00 AM&#39;;
  }

  NotificationsSettings.fromJson(Map&lt;String, dynamic&gt; json) {
    enabled = json[&#39;enabled&#39;];
    timeoutInDays = json[&#39;timeoutInDays&#39;];
    delayInHours = json[&#39;delayInHours&#39;];
    startTime = json[&#39;startTime&#39;];
  }

  Map&lt;String, dynamic&gt; toJson() {
    final Map&lt;String, dynamic&gt; data = new Map&lt;String, dynamic&gt;();
    data[&#39;enabled&#39;] = this.enabled;
    data[&#39;timeoutInDays&#39;] = this.timeoutInDays;
    data[&#39;delayInHours&#39;] = this.delayInHours;
    data[&#39;startTime&#39;] = this.startTime;
    return data;
  }

  @override
  bool operator ==(Object other) =&gt;
      other is NotificationsSettings &amp;&amp;
      other.runtimeType == runtimeType &amp;&amp;
      other.enabled == enabled &amp;&amp;
      other.timeoutInDays == timeoutInDays &amp;&amp;
      other.delayInHours == delayInHours &amp;&amp;
      other.startTime == startTime;

  int get hashCode =&gt;
      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

尝试使用 freezedequatable 包重写你的 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: &#39;09:00 AM&#39;);
}
NotificationsSettings fromJson(Map&lt;String, dynamic&gt; json) {
return NotificationsSettings(
enabled: json[&#39;enabled&#39;] ?? this.enabled,
timeoutInDays: json[&#39;timeoutInDays&#39;] ?? this.timeoutInDays,
delayInHours: json[&#39;delayInHours&#39;] ?? this.delayInHours,
startTime: json[&#39;startTime&#39;] ?? this.startTime,
);
}
Map&lt;String, dynamic&gt; toJson() {
final Map&lt;String, dynamic&gt; data = new Map&lt;String, dynamic&gt;();
data[&#39;enabled&#39;] = this.enabled;
data[&#39;timeoutInDays&#39;] = this.timeoutInDays;
data[&#39;delayInHours&#39;] = this.delayInHours;
data[&#39;startTime&#39;] = this.startTime;
return data;
}
@override
bool operator ==(Object other) =&gt;
other is NotificationsSettings &amp;&amp;
other.runtimeType == runtimeType &amp;&amp;
other.enabled == enabled &amp;&amp;
other.timeoutInDays == timeoutInDays &amp;&amp;
other.delayInHours == delayInHours &amp;&amp;
other.startTime == startTime;
int get hashCode =&gt;
Object.hash(enabled, timeoutInDays, delayInHours, startTime);
NotificationsSettings copyWith(
{bool? enabled,
int? timeoutInDays,
int? delayInHours,
String? startTime}) =&gt;
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.

huangapple
  • 本文由 发表于 2023年6月5日 22:26:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76407438.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定