实施 ChangeNotifier 与 StateNotifier

Zef*_*ndo 13 flutter riverpod

我非常熟悉Provider包并将其与ChangeNotifier.

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

  1. 切换加载
  2. 切换图像加载
  3. 切换 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();
Run Code Online (Sandbox Code Playgroud)

如果我使用ChangeNotifier,我只需要创建 1 个文件并调用类似的方法globalChangeNotifier.METHOD()或类似的值globalChangeNotifier.value

但现在,我已经了解了Riverpod包,并且在文档中,它使用StateNotifier.

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

  1. provider_isloading.dart,
  2. provider_isimageloading.dart
  3. provider_obsecurepassword.dart

使用状态通知器

// 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());
Run Code Online (Sandbox Code Playgroud)

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

GlobalStateNotifer.dart

export './provider_loading.dart';
export './provider_imageloading.dart';
export './provider_obsecurepassword.dart';
Run Code Online (Sandbox Code Playgroud)

我的问题是,正如我之前解释的那样,这是最佳实践吗?

我的文件夹结构

我的文件夹结构

Ale*_*ord 8

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

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

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

  void toggleImageLoading(bool value) {
    state = value;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

但是,如果您希望保留当前的方法,则可以创建一个类 ,A其中包含 3 个 bool 值和一个扩展的类StateNotifier<A>

例如:

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
    }
  }
}

Run Code Online (Sandbox Code Playgroud)

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