介绍

官方的写得很好,所以基本上我会看看

目标受众是 Riverpod 初学者~在大气中使用 Riverpod 的人

什么是Riverpod

用于管理应用程序状态的包
这里的状态指的是整数、字符串、用户定义的类和值。
具体有什么好处官方的我懂了
常规 StatefulWidget 有 State 并管理那里的状态
尝试从另一个 StatefulWidget 获取状态会导致代码复杂
要解决这个问题,请使用 flutter_riverpod
可以从应用程序中的任何位置作为提供者访问riverpod,从而可以轻松地在屏幕之间共享状态
此提供程序有多种类型

提供者中有什么以及如何使用它

提供者中有什么

  • 提供者
  • 状态提供者
  • StateNotifierProvider
  • ChangeNotifierProvider
  • 未来提供者
  • 流提供者

结论如下

  • 基于StateNotifierProvider,Provider如果你不想改变
  • StateProvider 与 StateNotifierProvider 的用途相同,但不推荐使用 StateProvider,因为它的值很容易被重写
  • 请勿使用 ChangeNotifier,除非是 Navigator2.0 等特定用途
  • FutureProvider 和 StreamProvider 用于异步,顾名思义

提供者

最基本的提供者
对所有提供者通用,在 Widget 内部使用 ref.watch 可以让你获取提供者提供的值,并在提供的值更新时自动重建小部件。还有 ref.read 和 ref.listen,后面会讲到。

// 定義
final pProvider = Provider<int>((ref) => 0);
// 参照
ref.watch(pProvideer) // == 0

此外,作为与其他 Provider 不同的功能,如果结果的值没有变化,则不会通知更新。
反之,即使更新了其他provider的值,结果是相同的值,也会跳过更改通知。
收到更新通知的小部件即使外观没有变化也会被重建,导致不必要的处理。
举个例子。
在像下面这样的代码的情况下,返回值将根据 hogeProvider 的值是 0 还是任何其他值而改变。
如果此返回值相同,则不通知观察 pProvider 的小部件。

final pProvider = Provider<bool>((ref) {
  return ref.watch(hogeProvider) == 0;
}
Widget build(BuildContext context, WidgetRef ref) {
  final isZero = ref.watch(pProvider);
  return isZero ? Text('zero!!!') : Text('aaa');
}

通过这样的实现,即使 hogeProvider 从 10 更改为 100 等,widget 重建也不会运行。
如果widget直接引用hogeProvider如下图,即使从10改成100也会重建。

Widget build(BuildContext context, WidgetRef ref) {
  final isZero = ref.watch(hogeProvider) == 0;
  return isZero ? Text('zero!!!') : Text('aaa');
}

如上所述,Provider 在正确使用时会导致性能提升。

国家提供者

像 Provider 一样提供价值
与 Provider 最大的不同是可以在外部更改值

final spProvider = StateProvider<int>((ref) => 0);
ref.watch(spProvider) // == 0
ref.read(spProvider.notifier).update((state) => state = ++state);
ref.watch(spProvider) // == 1

通过调用 update 来更新值,如第 3 行所示
如果更新,则会在 ref.watch 正在监视 spProvider 的地方发生重绘
缺点是状态可以从各个地方自由改写。
如果您对类型范围内的任何值都满意,请使用此选项,即如果您正在监视诸如整数、字符串和枚举之类的内容。
但是,即使是 int 类型,也不适合你期望通过 +1 更新的时候。
在这种情况下,请使用 StateNotifierProvider。

StateNotifierProvider

与 StateProvider 的区别在于将更新值的方法暴露给外部。

class CounterNotifier extends StateNotifier<int> {
  CounterNotifier(int initialState) : super(initialState);
  void increment() {
    state = ++state;
  }
}
final snpProvider = StateNotifierProvider<CounterNotifier, int>(
    (ref) => CounterNotifier(0));

ref.read(snpProvider.notifier).increment(); // increment 呼び出し

与 StateProvider 的区别在于限制信息暴露给外部。
StateProvider 允许你自由地重写值,但 StateNotifierProvider 将更新状态的方法暴露给外部。
严格来说,该值大概可以随意更改,只会发出警告。因此,根据实施者的判断,它与 StateProvider 没有什么不同。
在示例中显示的代码中,状态总是被称为 increment() 并且期望被 +1 更新。
如果你想拥有或多或少复杂的状态(比如一个类),你应该使用 StateNotifierProvider。
另一个优点是代码不会变得复杂,因为状态更新逻辑可以与监控端(小部件)分离。

ChangeNotifierProvider

由于不建议将其用于正常状态管理,此处省略。
除非您有特殊用例(Navigator2.0 等),否则请避免使用它。

未来提供者

用于异步检索值的提供程序
它与其他提供商的不同之处在于它如何处理来自手表端的数据。

final fProvider = FutureProvider((ref) async {
  await Future.delayed(const Duration(seconds: 2));
  return 0;
});
ref.watch(fProvider).when(
  data: (data) => Text(data.toString()),
  error: (error, stackTrace) => const Text('Error'),
  loading: () => const CircularProgressIndicator())

你用 ref.watch 得到的是 AsyncValue (AsyncValue 稍后描述)
除了值,还有加载、错误等状态,可以根据当前状态改变返回值。
在when子句中为watch监控的值定义数据、错误、加载,
data 是数据采集完成时的值
error 是数据获取过程中(异步处理过程中)发生错误时的值
loading 是数据检索期间的值
变得。

流提供者

顾名思义,一个处理 Streams 的提供者
主要用于处理定期更新值

final stProvider = StreamProvider<int>((ref) {
  int index = 0;
  return Stream.periodic(
    const Duration(seconds: 1),
    (computationCount) => ++index,
  );
});

final stProvider = StreamProvider<int>((ref) {
  Stream<int> fetchCounter() async* {
    for (int i = 0; i < 10; i++) {
      await Future.delayed(const Duration(seconds: 1));
      yield i;
    }
  }

  return fetchCounter();
});

// 監視
ref.watch(stProvider).when(
  data: (data) => Text(data.toString()),
  error: (error, stackTrace) => const Text('Error'),
  loading: () => const CircularProgressIndicator())

关于 AsyncValue

ref.watch(xxx).when()等可以根据状态改变返回值。一个例子如下所示。

ref.watch(sampleProvider).when(
  data: (data) => Text(data.toString()),
  error: (error, stackTrace) => const Text('Error'),
  loading: () => const CircularProgressIndicator())

copyWithPrevious

AsyncValue 具有保留先前状态的属性。
即使您在初始加载后更新,之前的值仍然会更新,因此您可以在屏幕上显示之前的值的同时进行更新。

state = AsyncLoading().copyWithPrevious(state)

使用喜欢
有状态转换返回到原始状态,并将 isLoading 设置为 true。
即使isLoading为真,也不会变成loading的when判断
如果你想改变小部件,你需要在数据中放入类似 ref.watch(xxx).isLoading ? hoge: fuga
还有isRefreshing,实现是(hasValue || hasError) && isLoading

数据检索

什么时候

定义所有三种状态:数据、错误、加载

ref.watch(sampleProvider).when(
  data: (data) => Text(data.toString()),
  error: (error, stackTrace) => const Text('Error'),
  loading: () => const CircularProgressIndicator())

可能是什么时候

何时总结加载和错误

ref.watch(sampleProvider).mayBeWhen(
  orElse: CircularProgressIndicator(),
  data: (value) => Text(value.toString())
);

当或空

加载错误时返回null

ref.watch(sampleProvider).whenOrNull(
  data: (value) => Text(value.toString())
);

使用 watch、read、listen、refresh 等。

手表

watch 监视和检索值。
如果值已更新,则重建小部件。

final newValue = ref.watch(sampleProvider);

read only 读取当前值并且不对其进行监控。
比如调用 StateNotifierProvider 的更新函数时,不需要监听值,所以使用 read

ref.read(sampleProvider.notifier).increment();

监听监视和检索值。
watch 返回更新后的值,但是
listen 可以定义一个返回值为 void 的回调函数,当值发生变化时会执行该回调函数。
此外,回调函数将是参数更新前后的值。
如果在构建外部使用,请改用 listenManual。

ref.listen(sampleProvider, (prev, next) {
  print('sampleProvider is update!');
});

刷新

重新评估和更新提供者。
如果不关心 refresh 的返回值,请改用 invalidate

final newValue = ref.refresh(sampleProvider);

无效

使提供程序状态无效并刷新。

ref.invalidate(sampleProvider);

存在

基本上已弃用。确定提供程序是否已初始化。

关于 .autoDispose

当没有什么可以参考时被丢弃的资格
如果没有 autoDispose ,它会在第一次被引用后再次被引用时保持初始状态,不再引用它。
过渡后返回画面时想重新获取值时使用

final sampleProvider = FutureProvider.autoDispose((ref) async{
  await Future.delayed(const Duration(seconds: 2));
  ref.onDispose(() {
    // 破棄されるタイミングの処理
  });
  return 10;
});

关于.family

从参数创建一个唯一的提供者。
对于拥有多个具有相似行为的提供者很有用
定义方法如下。对了,family和autodispose可以一起用

final familyProvider = FutureProvider.family<int, String>((ref, id)async {
  return dio.get('ttps://xxxxxxxxxxxxxx/$id');
});

使用此定义方法,您可以通过将 id 传递给 familyProvider 来创建根据 id 更改的提供程序。
当引用它时,传递一个相当于 id 的字符串,如下所示。

Widget build(BuildContext context, WidgetRef ref) {
  final res = ref.watch(familyProvider('sampleID'));
  // ...
}

使用 Riverpod 的 Flutter 应用架构模式考虑

这是我个人的看法,不是正确答案。

※关于Flutter的架构模式,就是这样!如果您能告诉我是否有正确的答案,那将很有帮助。

MVVM

基本上 MVVM 成为基础并添加了数据层
数据、模型、(存储库、)提供者、视图

数据

定义在任何层中使用的类,例如在冻结包中创建的类,作为数据层

模型

在Model层部分,用网络通信、本地存储等编写通信。
提供者公开了一个控制它的类的实例
业务逻辑类也属于此类别,并在 Provider 中公开

存储库

在Repository层,ref.read Model层调用流程并保存数据
这也使用 Provider 公开了实例
您可以通过传递 ref 在内部读取模型层实例

final sampleRepositoryProvider = Provider((ref) => SampleRepository(ref));
// SampleRepository class の実装は省略

提供者

所谓的 ViewModel 层充当 Repository 层和 View 层之间的桥梁,并持有状态。
我们之所以不命名 ViewModel 层是因为我们希望 VSCode 文件夹顺序为模型 -> 提供者 -> 从顶部查看
提供者使用各种提供者向视图层公开
同时发布指示要保存数据的过程

class CounterStateNotifier extends StateNotifier<int> {
  CounterStateNotifier(this._ref) : super(0);
  StateNotifierProviderRef _ref;

  void storeData() {
    _ref.read(sampleRepositoryProvider).store(state);
  }
}
final counterStateProvider = StateNotifierProvider<CounterStateNotifier,int>(
  (ref) => CounterStateNotifier(ref));

看法

通过引用和监控 Provider 层中暴露的提供者来定义屏幕配置

class SampleView extends ConsumerWidget {
  Widget build(BuildContext context, WidgetRef ref) {
    Scaffold(
      body: Center(
        child: Text(ref.watch(counterStateProvider).toString())
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ref.read(counterStateProvider.notifier).storeData();
        },
        child: const Icon(Icons.add),
      );
    )
  }
}

原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308632629.html

相关文章:

  • 2022-12-23
  • 2021-05-22
  • 2021-10-22
  • 2022-01-21
  • 2021-12-05
  • 2021-11-24
  • 2021-08-01
  • 2021-08-18
猜你喜欢
  • 2021-04-14
  • 2021-06-11
  • 2021-09-07
  • 2021-08-19
  • 2022-12-23
  • 2021-12-31
  • 2022-12-23
相关资源
相似解决方案