Riverpod 简单使用记录
从Provider到RiverPod
开发时Provider更多的是实现MVVM
架构,到Riverpod Viewmodel
的写法会跟之前有一定的区别。
开始
添加依赖
if (use hookWidget)
flutter pub add hooks_riverpod
flutter pub add flutter_hooks
else
flutter pub add flutter_riverpod
.then()=>
flutter pub add riverpod_annotation
flutter pub add dev:riverpod_generator
flutter pub add dev:build_runner
flutter pub add dev:custom_lint
flutter pub add dev:riverpod_lint
初始设置
main.dart
void main() {
runApp(const ProviderScope(child: MyApp()));
}
ProviderScope
为了Widgets能够读取到Providers,需要使用ProviderScope包裹整个AppWidget。
开发工具代码块插件(Flutter Riverpod Snippets)
到这里RiverPod的引入和初始化操作就已经完成。
简单示例
在RivierPod中,页面的Widget推荐使用以下两种:
ConsumerWidget
||HookConsumerWidget
(如果你使用HookWidget) 两种方式都等价于StatelessWidget
。
同样ConsumerStatefulWidget
||StatefulHookConsumerWidget
==StatefulWidget
。
以下示例为了代码简洁统一使用 ConsumerWidget
HomePage.dart
/// String
final helloWorldProvider = Provider<String>((ref) => 'Hello Riverpod!');
这行生成一个最简单的Provider,其中:
- Provider:大多数provider类型的基础,它暴露一个永远不会改变的常量;
- ref:flutter每一个Widget都有Buildcontext方法,它提供了上下文信息,但ref可在函数中读取到其他的provider,它有三种用法:
ref.read
:在忽略对象改变的情况下获取provider的值。如:onPress内改变provider值时不需要实时获取ref的值;ref.watch
:获取provider到的值并监听更改,当该值改变时,将重新构建订阅该值的widget或provider;ref.listen
:在provider上添加监视器,以执行诸如导航到新页面或每当provider更改时显示模态框等操作。
官方作者建议⚠️
尽可能使用ref.watch
而不是ref.read
或ref.listen
来实现你的功能。 通过依赖ref.watch
,你的应用变得既具有响应性又具有声明性,这使得项目会更易于维护。
实际使用
ref.watch
使用于Widget的构建渲染中,因为它是订阅监听的,而如果使用ref.read
来渲染组件的话会发现它有一定延迟,大概一秒刷新一次的样子,所以最好将ref.read
用于事件的触发函数中。
class HomePage extends ConsumerWidget {
const HomePage({super.key});
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.pink[50],
title: const Text('RiverPod Demo'),
),
body: Center(
child: Column(
children: [Text(ref.watch(helloWorldProvider))],// ref.watch
),
));
}
}
不同类型的Provider
Provider类型 | 创建Provider的函数 | 使用场景 |
---|---|---|
Provider | 返回任意类型 | 服务类 / 计算属性 (过滤的列表) |
StateProvider | 返回任意类型 | 过滤条件/简单状态对象 |
FutureProvider | 返回任意类型的Future | API调用的结果 |
StreamProvider | 返回任意类型的Stream | API返回的Stream |
StateNotifierProvider | 返回StateNotifier的子类 | 一种复杂的状态对象,除了通过接口之外,它是不可变的 |
ChangeNotifierProvider | 返回ChangeNotifier的子类 | 需要可变的复杂状态对象 |
部分Provider使用示例
如果想要更新一个provider的值,可以使用.notifier
,该方法会返回一个StateController<T>
,API文档。
.update
会返回provider的对象。
ref.read(countProvider.notifier).update((count) => count += 1);
StateProvider
final countProvider = StateProvider<int>((ref) => 0);
TextButton(
onPressed: () {
ref.read(countProvider.notifier).update((count) => count += 1);
},
child: Text(ref.watch(countProvider).toString()),
),
FutureProvider
final userNicknameProvider = FutureProvider((ref) async {
// 此处模拟一个耗时任务
return await Future.delayed(const Duration(seconds: 1), () {
final nickname = 'Jhin';
return nickname;
});
});
Text('Nickname:${ref.watch(userNicknameProvider).value ?? "加载中..."}'),
在一秒后Text的文本内容会更新为“Jhin”
StateNotifierProvider
通常要改变一个具体的类是需要使用StateNotifierProvider
,如用户信息类:UserInfo
。
可以看到使用StateNotifierProvider
不需要像Provider一样自己调用notifyListeners()
。
class UserInfo {
UserInfo({
required this.nickname,
required this.id,
required this.count,
})
final String nickname;
final int id;
int count;
UserInfo increment() {
count++;
return UserInfo(nickname: nickname, id: id, count: count);
}
UserInfo decrement() {
count--;
return UserInfo(nickname: nickname, id: id, count: count);
}
String toString() {
return "{nickname:$nickname},{id:$id},{count:$count}";
}
UserInfo userInfo(WidgetRef ref) {
return UserInfo(nickname: nickname, id: id, count: count);
}
}
用户类有几个简单属性:用户名,ID,计数器。
如果想展示用户信息并监听计数器的改变就需要创建一个StateNotifierProvider
final userStateNotifyProvider = StateNotifierProvider<UserController, UserInfo>(
(ref) => UserController(UserInfo(nickname: 'RaBBit', id: 1, count: 20)));// 初始用户信息
class UserController extends StateNotifier<UserInfo> {// 中间层
UserController(super.state);
void increment() {
state = state.increment();
}
void decrement() {
state = state.decrement();
}
}
TextButton(
onPressed: () {
final UserController controller = ref.watch(userStateNotifyProvider.notifier);
controller.increment();
},
child: Text('Count:${ref.watch(userStateNotifyProvider.select((user) => user.count))}'))
其中provider.select
方法可以只监听一个provider中具体的值,可以减少数据刷新时的渲染消耗。
组合Provider
provider可以通过ref.watch
获取另一个provider
/// 异步联合Provider
final userNicknameProvider = FutureProvider((ref) async {
return await Future.delayed(const Duration(seconds: 1), () {
final nickname = ref.watch(userStateNotifyProvider).nickname;
return nickname;
});
});
参考
RiverPod官网
Flutter Riverpod 2.0:终极指南
RiverPod 使用记录
作者也还在学习RiverPod的过程