Skip to content

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. 复习检查题 ​

  1. Provider 和 StateProvider 的边界是什么?

    答:Provider 是只读注入/派生,不承载可变状态;StateProvider 承载最简单的可变标量态,再复杂就该收敛到 Notifier。

  2. 为什么说派生 provider 是 Riverpod 「可组合」的核心?

    答:因为派生 provider watch 上游后,上游任何变化都会自动触发重算和下游重建,状态可以像管道一样串联而无需手工同步。

  3. watch / read / invalidate 各应该出现在哪里?

    答:watch 放在 build 中建立订阅;read 放在回调中执行副作用;invalidate 用于让异步 provider 显式重放。

7. 对应知识库文档 ​

站点构建时间:2026/8/24 23:43:17