Appearance
flutter-riverpod-basics
1. 实验目标与工程要点
在同一个页面内并排演示 Riverpod 四种最常用的 Provider 形态,验证「约束强度」差异,而不是背 API:
Provider:只读依赖注入 / 派生值(doubleCountProvider演示 provider 组合链)StateProvider:最小可变状态(计数器 +1 / 重置)FutureProvider:异步一次性数据,AsyncValue.when三分态渲染 +invalidate重放StateNotifier+StateNotifierProvider:状态与行为收敛到一个类,状态只能通过方法修改
工程要点:
- 页面自持
ProviderScope,不污染宿主全局容器 watch用于订阅重建,read用于回调里的副作用 —— 两者的使用位置是评审高频点- 状态修改入口唯一(notifier 方法),UI 无法绕过行为直接改 state
2. 关键实现速览
2.1 四种 Provider 的声明对比
dart
// 1. 只读依赖注入 + 派生值(watch 上游,自动重算)
final greetingProvider = Provider<String>((ref) => 'Hello Riverpod');
final doubleCountProvider = Provider<int>((ref) => ref.watch(counterProvider) * 2);
// 2. 最小可变状态
final counterProvider = StateProvider<int>((ref) => 0);
// 3. 异步一次性数据(AsyncValue 三分态由容器托管)
final userNameProvider = FutureProvider<String>((ref) async => fetchUserName());
// 4. 状态 + 行为收敛
class TodoListNotifier extends StateNotifier<List<String>> {
TodoListNotifier() : super(const []);
void add(String item) => state = [...state, item];
}
final todoListProvider =
StateNotifierProvider<TodoListNotifier, List<String>>((ref) => TodoListNotifier());为什么看这段:四种形态覆盖了「只读注入 → 可变标量 → 异步数据 → 业务状态对象」的完整谱系;选型的本质是选约束强度,不是选写法快慢。
2.2 watch / read / invalidate 的使用位置
dart
// build 里订阅:值变化 → 本组件重建
final count = ref.watch(counterProvider);
final doubled = ref.watch(doubleCountProvider); // 派生链自动跟随
// 回调里副作用:不订阅、只取 notifier
onPressed: () => ref.read(counterProvider.notifier).state++,
// 让 FutureProvider 重新执行:观察 loading → data 再走一遍
onPressed: () => ref.invalidate(userNameProvider),为什么看这段:把 read 写进 build 会漏掉更新、把 watch 写进回调会报错;这段代码展示了三者的标准落位。
完整源码:riverpod_basics_demo_page.dart
3. 运行方式
bash
cd labs/flutter/host
flutter pub get
flutter run -d macos # 或 ios / android
# 首页进入「Riverpod 基础用法对比」4. 预期现象与观察重点
场景 A:进入页面
预期顺序:
text
四张卡片同时渲染:
Provider 卡片显示 Hello Riverpod + count=0/doubleCount=0
StateProvider 卡片显示 count = 0
FutureProvider 先 loading 约 1 秒 → 当前用户: user-1001
TodoList 卡片预置一条待办观察点:
- FutureProvider 的 loading 态由容器托管,页面没有手写
isLoading字段
场景 B:点击 StateProvider 的 + 按钮
预期行为:
count与 Provider 卡片里的count/doubleCount同步变化
观察点:
- 一处
state++,两张卡片同时重建 —— 这就是派生 provider 的组合链效应
场景 C:点击「重新请求 (invalidate)」
预期行为:
- 用户行回到 loading ≈1 秒后重新出现 data
观察点:
invalidate不需要手动清缓存、重设 loading 标志
场景 D:添加 / 删除待办
预期行为:
- 列表通过「整表替换」(新 List)驱动重建,而不是原地 mutate
观察点:
- 状态只能经
TodoListNotifier.add/removeAt修改 —— 行为入口唯一,测试时 mock 一个 notifier 即可
5. 常见误区
| 误区 | 实际情况 |
|---|---|
在 build 里用 ref.read | 读一次就不再更新,UI 会「看起来坏了」;build 内应 watch |
| 简单开关也上 StateNotifier | 标量 UI 态用 StateProvider 足够,约束要与复杂度匹配 |
直接改 notifier.state 绕过方法 | 行为入口散落后无法单测与排查;修改应收敛到 notifier 方法 |
| FutureProvider 当轮询器用 | 它是一次性异步数据;周期刷新应显式 invalidate 或换 StreamProvider |
6. 复习检查题
Provider和StateProvider的边界是什么?答:
Provider是只读注入/派生,不承载可变状态;StateProvider承载最简单的可变标量态,再复杂就该收敛到 Notifier。为什么说派生 provider 是 Riverpod 「可组合」的核心?
答:因为派生 provider
watch上游后,上游任何变化都会自动触发重算和下游重建,状态可以像管道一样串联而无需手工同步。watch/read/invalidate各应该出现在哪里?答:
watch放在build中建立订阅;read放在回调中执行副作用;invalidate用于让异步 provider 显式重放。