实施 ChangeNotifier 与 StateNotifier

Posted

技术标签:

【中文标题】实施 ChangeNotifier 与 StateNotifier【英文标题】:Implement ChangeNotifier vs StateNotifier 【发布时间】:2020-11-06 20:08:17 【问题描述】:

我对@9​​87654321@ 包非常熟悉,并将其与ChangeNotifier 结合使用。

假设我有 3 个 getter 和具有不同功能的方法:

    切换加载 切换图像加载 切换 ObsecurePassword

使用 ChangeNotifer

import 'package:flutter/foundation.dart';

class GlobalChangeNotifier extends ChangeNotifier 
  bool _isLoading = false;
  bool _isImageLoading = false;
  bool _isObsecurePassword = false;

  bool get isLoading => _isLoading;
  bool get isImageLoading => _isImageLoading;
  bool get isObsecurePassword => _isObsecurePassword;

  void setLoading(bool value) 
    _isLoading = value;
    notifyListeners();
  

  void setImageLoading(bool value) 
    _isImageLoading = value;
    notifyListeners();
  

  void setObsecurePassword(bool value) 
    _isObsecurePassword = !value;
    notifyListeners();
  


final globalChangeNotifier = GlobalChangeNotifier();

如果我使用ChangeNotifier,我只需要创建一个文件,然后调用globalChangeNotifier.METHOD() 之类的方法或globalChangeNotifier.value 之类的值。

但是现在,我了解了Riverpod 包,并且在文档中,它使用的是StateNotifier

我想将我以前的代码从ChangeNotifier 迁移到StateNotifier。 但据我了解,StateNotifier 只能保存 1 种类型的数据,所以如果我想迁移上面的代码,我应该创建 3 个文件,比如说:

    provider_isloading.dart, provider_isimageloading.dartprovider_obsecurepassword.dart

使用 StateNotifier

// provider_isloading.dart
class IsImageLoading extends StateNotifier<bool> 
  IsImageLoading() : super(false);

  void toggleImageLoading(bool value) 
    state = value;
  


final isImageLoadingProvider = StateNotifierProvider((ref) => IsImageLoading());

// provider_isimageloading.dart

class IsLoading extends StateNotifier<bool> 
  IsLoading() : super(false);
  void toggleLoading(bool value) => state = value;


final isLoadingProvider = StateNotifierProvider((ref) => IsLoading());

// provider_obsecurepassword.dart
class IsObsecurePassword extends StateNotifier<bool> 
  IsObsecurePassword() : super(false);

  void toggleObsecurePassword(bool value) 
    state = !value;
  


final isObsecurePasswordProvider = StateNotifierProvider((ref) => IsObsecurePassword());

我还需要创建 1 个文件来导出所有这些文件:

GlobalStateNotifer.dart

export './provider_loading.dart';
export './provider_imageloading.dart';
export './provider_obsecurepassword.dart';

我的问题是,按照我之前解释的那样做是最佳做法吗?

我的文件夹结构

【问题讨论】:

【参考方案1】:

在使用 Riverpod 时,在它们提供的类上创建静态提供程序非常有意义。从您的示例中,您可以重构为:

class IsImageLoading extends StateNotifier<bool> 
  IsImageLoading() : super(false);

  static final provider = StateNotifierProvider((ref) => IsImageLoading());

  void toggleImageLoading(bool value) 
    state = value;
  

您还应该考虑是否需要在您实际使用它们的课程之外提供您的提供程序。有些事情告诉我,除了登录页面之外,您可能不会在任何地方使用您的密码提供程序。考虑在该类中创建一个私有提供程序。

但是,如果您希望保持当前方法,您可以创建一个类 A,其中包含 3 个布尔值和一个扩展 StateNotifier&lt;A&gt; 的类。

例如:

enum LoadingType  A, B, C 

class LoadingToggles 
  bool A, B, C;

  LoadingToggles(this.A = false, this.B = false, this.C = false);

  static final provider = StateNotifierProvider.autoDispose((ref) => LoadingState(LoadingToggles()));


class LoadingState extends StateNotifier<LoadingToggles> 
  LoadingState(LoadingToggles state) : super(state ?? LoadingToggles());

  void toggle(LoadingType type) 
    switch (type) 
      case LoadingType.A:
        state.A = !state.A;
        break;
      case LoadingType.B:
        state.B = !state.B;
        break;
      case LoadingType.C:
        state.C = !state.C;
        break;
      default:
        // Handle error state
    
  


最后,只想补充一点,可能有更好的方法来处理整体加载。考虑是否可以将 FutureProvider/StreamProvider 与 Riverpod 的 AsyncValue 一起使用,而不是手动切换加载状态。

【讨论】:

以及如何在 UI 上调用静态方法?我尝试isImageLoading().provider 它没有定义。 @ZeffryReynando 好吧,那是因为它不是!静态方法是为类本身定义的,不能从类实例中调用。 IsImageLoading.provider 是您正在寻找的。虽然这个概念不是 dart 独有的,但这里是 dart 中 static 关键字上的 short tutorial。 您的代码对我来说很有意义。但是我想确认一件事,你能分析我的代码吗?这已经是很好的做法了。 gist.github.com/zgramming/212cd583ac20e4fb8fdd8b7a9150522e 。已经按照您的建议创建另一个类以将所有属性存储在单个文件中。 用我的建议评论了你的要点。 您的建议代码不起作用,我必须退回到旧代码【参考方案2】:

我认为你应该使用像下面的代码这样的不可变类

@immutable
abstract class GlobalState 
  const GlobalState();


class IsImageLoading extends GlobalState 
  const IsImageLoading();


class IsLoading extends GlobalState 
  const IsLoading();


class IsObsecurePassword extends GlobalState 
  const IsObsecurePassword();

您的 StateNotifier 将如下所示

class GlobalStateNotifier extends StateNotifier<GlobalState> 
  GlobalStateNotifier(GlobalState state) : super(state);

  void changeState(GlobalState newState) 
    state = newState;
  

【讨论】:

【参考方案3】:

响应很晚,但我现在正在搜索相同的内容,但我没有找到 正确 方法来使用 Riverpod 或 StateNotifier,但可能你应该像使用 ChangeNotifier 一样使用,或者flutter_bloc 用户他们应该使用类似flutter_bloc 逻辑...或两者兼而有之。

对于这种情况以及我所阅读的内容,我将只使用一个 dart 文件来放置所有代码,例如您的 changenotifier 类,并为IsImageLoading, isLoading, isObscurePassword 制作简单的状态提供程序

在另一个 MyViewProvider 类中,您可以更改这些 statesProvider 的状态,或者不创建它并直接在视图中更改状态。


final isImageLoadingProvider = StateProvider((ref) => false);

final isLoadingProvider = StateProvider((ref) => false);

final isObscurePasswordProvider = StateProvider((ref) => false);

final myViewProvider = Provider<MyViewProvider>((ref) 
  return MyViewProvider(ref.read);
);

class MyViewProvider 
  MyViewProvider(this._read);

  final Reader _read;

  void setLoading(bool value) 
    _read(isLoadingProvider).state = value;
  

  void setImageLoading(bool value) 
    _read(isImageLoadingProvider).state = value;
  

  void setObscurePassword() 
    final isObscure = _read(isLoadingProvider).state;
    _read(isObscurePasswordProvider).state = !isObscure;
  

如果有任何 cmets 或改进,我将不胜感激

【讨论】:

以上是关于实施 ChangeNotifier 与 StateNotifier的主要内容,如果未能解决你的问题,请参考以下文章

Flutter Listen 的 ChangeNotifier 类,它是 ChangeNotifier 的一个属性

ChangeNotifier 安装等效?

Flutter 数据监听器ChangeNotifier

为大量听众优化的 ChangeNotifier 的替代方案?

如何关闭 ChangeNotifier Provider Flutter 中的函数

如何在 ChangeNotifier 中使用 Futures?