介绍
官方的写得很好,所以基本上我会看看
目标受众是 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