Flutter Riverpod 状态管理深入分析
本文档深入解析 Flutter Riverpod 状态管理库的核心原理、各类 Provider 的用法,并通过多个场景示例详细讲解实现方式。基于
riverpod: ^3.2.1版本。
目录
- Riverpod 是什么,为什么要用
- 整体工作机制
- Provider 类型详解
- Ref 与读取方式
- 修饰符:family 与 autoDispose
- Consumer 与 Widget 集成
- AsyncValue 与异步状态处理
- 场景一:基础计数器
- 场景二:异步用户资料
- 场景三:购物车与依赖组合
- 场景四:family 参数化
- 常见问题与最佳实践
- Riverpod 3.2.x 版本要点
- 附录:快速参考
文档说明与版本基线
文中使用的包版本基线如下:
dependencies:
flutter:
sdk: flutter
flutter_riverpod: ^3.2.1
riverpod_annotation: ^3.2.1 # 可选,用于代码生成
dev_dependencies:
flutter_test:
sdk: flutter
riverpod_generator: ^3.2.1 # 可选,代码生成
build_runner: ^2.4.0
riverpod_lint: ^3.2.1 # 可选,静态分析
这些版本分别对应:
flutter_riverpod: ^3.2.1:本文中的Provider、Notifier、ref.watch、ConsumerWidget等用法按 Riverpod 3.x API 说明。riverpod: ^3.2.1:核心包,由flutter_riverpod自动依赖。- Riverpod 3.2.1(2026-02-03)主要修复:恢复暂停的 provider 后可能不再通知监听器的 bug。
1. Riverpod 是什么,为什么要用
Riverpod 是由 Remi Rousselet 开发的响应式缓存与数据绑定框架,是 Provider 的继任者,旨在解决 Provider 的诸多限制。
一句话概括它的工作方式:
Provider 是“带缓存的函数”,通过
ref组合依赖、自动失效;UI 通过ref.watch订阅,状态变化时自动重建。
在以下场景里,Riverpod 往往比 Provider、BLoC 更合适:
- 需要编译期安全:Provider 依赖
BuildContext,Riverpod 不依赖,可在任意处使用 - 需要多实例同类型:同一类型可有多个 Provider,通过变量名区分
- 需要自动缓存与失效:
ref.watch自动建立依赖,依赖变化时自动重建 - 需要测试友好:所有 Provider 可 override,无需 Widget 树
- 需要异步优先:
FutureProvider、AsyncNotifier、AsyncValue原生支持 - 需要自动释放:
autoDispose在无监听时自动销毁状态
它的核心优势:
| 特性 | 说明 |
|---|---|
| 编译期安全 | 不依赖 BuildContext,可在 initState、测试、纯 Dart 中使用 |
| 响应式缓存 | Provider 即“带缓存的函数”,自动管理生命周期 |
| 依赖自动追踪 | ref.watch 建立依赖图,依赖变化自动失效并重建 |
| 异步原生支持 | FutureProvider、AsyncNotifier、AsyncValue 内置 |
| 可测试性 | ProviderScope + overrides 轻松 mock |
| 自动释放 | autoDispose 无监听时自动 dispose |
| 参数化 | family 支持带参数的 Provider |
2. 整体工作机制
2.1 数据流概览
Provider 定义(带缓存的函数)
↓
ProviderScope(存储 Provider 状态)
↓
ref.watch / ref.read / ref.listen
↓
UI 重建 / 副作用 / 仅读取
2.2 核心概念
- Provider:状态的描述,本质是“带缓存的函数”,顶层声明,不可变
- ProviderContainer:存储 Provider 实际状态,Flutter 中由
ProviderScope创建 - Ref:Provider 之间、Provider 与 UI 之间的桥梁,用于
watch、read、listen、invalidate等 - Consumer:
ConsumerWidget、Consumer、ConsumerStatefulWidget等,提供WidgetRef
ref 的来源:在不同上下文中,
ref的获取方式不同,详见 4.0 ref 的来源。
2.3 内部实现原理简述
Riverpod 的核心可以理解为:
- Provider 是纯函数描述:Provider 本身不可变,只是“如何计算值”的描述,不持有状态。
- ProviderContainer 持有实际状态:每个
ProviderScope对应一个ProviderContainer,内部维护 Provider 的缓存与依赖图。 - Ref 是桥梁:
ref.watch建立“当前计算 → 依赖的 Provider”的边,依赖变化时自动标记失效并触发重建。 - 懒加载:Provider 首次被
watch或read时才执行 build 函数,之后返回缓存值。 - 依赖图驱动:当 A 依赖 B,B 变化时 A 自动失效,下次被访问时重新计算。
简化模型:
// 概念上,Provider 类似(ref 由框架注入):
class SimpleProvider<T> {
T? _cache;
final T Function(Ref ref) _build;
T getValue(Ref ref) {
if (_cache == null) _cache = _build(ref);
return _cache!;
}
}
真实实现会更复杂(依赖追踪、autoDispose、family 等),但核心思想是:带缓存的函数 + 依赖图驱动的自动失效。
2.4 与 Provider 的对比
| 维度 | Provider | Riverpod |
|---|---|---|
| 依赖 | 依赖 BuildContext、Widget 树 | 不依赖,可在任意处使用 |
| 多实例 | 同类型需不同 Provider 类型包装 | 同类型可有多个 Provider,变量名区分 |
| 测试 | 需要挂载 Widget 树 | 直接 ProviderContainer + overrides |
| 异步 | FutureProvider、StreamProvider | 同上,另增 AsyncNotifier |
| 缓存 | 需手动管理 | 内置缓存与自动失效 |
3. Provider 类型详解
Riverpod 提供 6 种 Provider 变体,按同步/异步/流式和不可变/可变划分:
| 同步 | Future | Stream | |
|---|---|---|---|
| 不可变 | Provider | FutureProvider | StreamProvider |
| 可变 | NotifierProvider | AsyncNotifierProvider | StreamNotifierProvider |
3.1 Provider(同步、不可变)
最基础的 Provider,用于暴露不可变值或单例服务。ref 是回调函数的第一个参数,由框架在构建时注入。
// ref:Provider 回调的第一个参数
final helloWorldProvider = Provider((ref) => 'Hello world');
final apiClientProvider = Provider((ref) => ApiClient());
3.2 FutureProvider(异步、不可变)
用于一次性异步数据,如网络请求、本地存储读取。
final userProvider = FutureProvider<User>((ref) async {
final api = ref.watch(apiClientProvider);
return api.fetchUser();
});
消费时得到 AsyncValue<User>,可区分 loading、data、error。
3.3 StreamProvider(流式、不可变)
用于流式数据,如 WebSocket、Firebase、数据库监听。
final messagesProvider = StreamProvider<List<Message>>((ref) {
return ref.watch(chatServiceProvider).messageStream;
});
3.4 NotifierProvider(同步、可变)
用于可变状态,用户交互后需要修改状态。替代旧的 StateNotifier。
class CounterNotifier extends Notifier<int> {
@override
int build() => 0;
void increment() => state++;
void decrement() => state--;
}
final counterProvider = NotifierProvider<CounterNotifier, int>(CounterNotifier.new);
3.5 AsyncNotifierProvider(异步、可变)
用于异步初始化的可变状态,如需要先拉取数据再允许用户操作。
// ref:AsyncNotifier 基类提供的属性
class UserProfileNotifier extends AsyncNotifier<UserProfile> {
@override
Future<UserProfile> build() async {
final api = ref.read(apiClientProvider);
return api.fetchProfile();
}
Future<void> updateName(String name) async {
state = const AsyncLoading();
try {
final profile = await ref.read(apiClientProvider).updateName(name);
if (!ref.mounted) return;
state = AsyncData(profile);
} catch (e, st) {
if (!ref.mounted) return;
state = AsyncError(e, st);
}
}
}
final userProfileProvider = AsyncNotifierProvider<UserProfileNotifier, UserProfile>(UserProfileNotifier.new);
3.6 StreamNotifierProvider(流式、可变)
用于需要修改流的场景,较少使用。
4. Ref 与读取方式
4.0 ref 的来源
ref 是 Riverpod 注入的引用对象,用于访问 Provider、建立依赖、监听变化等。在不同上下文中,ref 的获取方式不同:
| 上下文 | ref 的来源 | 示例 |
|---|---|---|
| Provider 回调 | 回调函数的第一个参数 | Provider((ref) => ref.watch(...)) |
| Notifier / AsyncNotifier | 基类提供的 ref 属性 | class X extends Notifier<T> { build() => ref.read(...) } |
| ConsumerWidget | build 方法的第二个参数 WidgetRef ref | Widget build(BuildContext context, WidgetRef ref) |
| ConsumerStatefulWidget | ConsumerState 的 ref 属性 | class _MyState extends ConsumerState<MyPage> { ref.watch(...) } |
| Consumer | builder 的第二个参数 | Consumer(builder: (context, ref, _) => ...) |
| overrideWith / overrideWithBuild | override 回调的参数 | provider.overrideWith((ref) => value) |
- Provider 回调:框架在首次构建 Provider 时调用你的函数,并传入
ref。 - Notifier / AsyncNotifier:继承基类后,
this.ref由框架在创建 Notifier 实例时注入,在build()和实例方法中均可使用。 - Consumer 系列:
ConsumerWidget、ConsumerStatefulWidget、Consumer是 Riverpod 提供的桥接组件,将ProviderScope中的ref传给子组件。
4.1 ref.watch
作用:监听 Provider,当值变化时触发当前 Widget 或 Provider 重建。
class CounterDisplay extends ConsumerWidget {
const CounterDisplay({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Text('$count');
}
}
- 适用于:需要根据状态更新 UI 的场景
- 在 Provider 内使用:建立依赖,依赖变化时该 Provider 会重建
4.2 ref.read
作用:仅读取当前值,不监听,不会因变化而重建。
FloatingActionButton(
onPressed: () => ref.read(counterProvider.notifier).increment(),
child: const Icon(Icons.add),
)
- 适用于:事件回调、
initState、一次性操作 - ⚠️ 不要在 build 中用于需要监听的值(应用
ref.watch)
4.3 ref.listen
作用:监听变化并执行副作用,不触发重建。类似 BlocListener。
// 需在 ConsumerWidget / Consumer 的 build 中调用,才能使用 context
ref.listen<AsyncValue<User>>(userProvider, (previous, next) {
next.whenOrNull(
data: (user) => print('User loaded: $user'),
error: (e, _) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
}
},
);
});
- 适用于:导航、SnackBar、Dialog 等一次性副作用
weak: true:不强制初始化 Provider,仅在有其他监听者时才初始化
4.4 ref.invalidate / ref.refresh
ref.invalidate(provider):标记 Provider 失效,下次被 watch 时重建,不立即重建ref.refresh(provider):立即重建并返回新值
ref.invalidate(userProvider); // 延迟重建,下次 watch 时重建
ref.refresh(userProvider); // 立即重建;对 FutureProvider 返回 AsyncValue<T>
5. 修饰符:family 与 autoDispose
5.1 family(参数化)
用于根据参数创建不同实例,如按 ID 获取用户。
final userProvider = FutureProvider.family<User, String>((ref, userId) async {
final api = ref.watch(apiClientProvider);
return api.fetchUser(userId);
});
// 使用
ref.watch(userProvider('user-123'));
5.2 autoDispose(自动释放)
当 Provider 不再被监听时,自动销毁其状态,释放资源。
final searchResultsProvider = FutureProvider.autoDispose<List<Product>>((ref) async {
final query = ref.watch(searchQueryProvider);
return ref.read(apiClientProvider).search(query);
});
- 适用:页面级、列表项级等短期状态
ref.keepAlive():在 autoDispose 的 Provider 内调用,可阻止自动释放ref.onDispose():注册 dispose 时的清理逻辑
5.3 组合使用
final productProvider = FutureProvider.autoDispose.family<Product, String>((ref, id) async {
return ref.read(apiClientProvider).fetchProduct(id);
});
6. Consumer 与 Widget 集成
6.1 ConsumerWidget
无状态 Widget,build 方法多一个 WidgetRef ref 参数(即 ref 的来源)。
class MyPage extends ConsumerWidget {
const MyPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Scaffold(
body: Center(child: Text('$count')),
floatingActionButton: FloatingActionButton(
onPressed: () => ref.read(counterProvider.notifier).increment(),
child: const Icon(Icons.add),
),
);
}
}
6.2 ConsumerStatefulWidget
有状态 Widget,ref 来自 ConsumerState 基类,在 initState、build 等生命周期中均可使用。
class MyPage extends ConsumerStatefulWidget {
const MyPage({super.key});
@override
ConsumerState<MyPage> createState() => _MyPageState();
}
// ref:ConsumerState 基类提供的属性
class _MyPageState extends ConsumerState<MyPage> {
@override
void initState() {
super.initState();
Future.microtask(() {
ref.read(someProvider.notifier).fetch();
});
}
@override
Widget build(BuildContext context) {
final data = ref.watch(someProvider);
return Text('$data');
}
}
6.3 Consumer
在任意 Widget 的 build 中嵌入,ref 来自 builder 的第二个参数。
// ref:Consumer 的 builder 第二个参数
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
@override
Widget build(BuildContext context) {
return Consumer(
builder: (context, ref, _) {
final value = ref.watch(myProvider);
return Text('$value');
},
);
}
}
6.4 ProviderScope
应用根节点必须包裹 ProviderScope,用于存储 Provider 状态。
void main() {
runApp(
ProviderScope(
child: const MyApp(),
),
);
}
7. AsyncValue 与异步状态处理
FutureProvider 和 AsyncNotifierProvider 暴露的值类型为 AsyncValue<T>。
7.1 AsyncValue 三种状态
AsyncLoading:加载中,可携带previous用于刷新时保留旧数据AsyncData<T>:成功,包含valueAsyncError:失败,包含error和stackTrace
7.2 常用 API
// 模式匹配
asyncValue.when(
data: (data) => Text('$data'),
loading: () => const CircularProgressIndicator(),
error: (e, st) => Text('Error: $e'),
);
// 简化版
asyncValue.whenOrNull(
data: (data) => Text('$data'),
);
// 获取值,loading/error 时抛错
final value = asyncValue.requireValue;
// 获取值,error 时返回 null
final value = asyncValue.value;
// 判断
asyncValue.hasValue;
asyncValue.hasError;
asyncValue.isLoading;
asyncValue.isReloading; // 刷新中(有旧数据)
7.3 结合 ref.watch 组合异步 Provider
Riverpod 3.1+ 支持在 FutureProvider 内使用 AsyncValue.requireValue 同步组合异步 Provider:
final sumProvider = FutureProvider<int>((ref) async {
AsyncValue<int> a = ref.watch(aProvider);
AsyncValue<int> b = ref.watch(bProvider);
// requireValue 在 loading/error 时会抛错,需确保依赖已就绪
return a.requireValue + b.requireValue;
});
8. 场景一:基础计数器
8.1 定义 Notifier
class CounterNotifier extends Notifier<int> {
@override
int build() => 0;
void increment() => state++;
void decrement() => state--;
}
final counterProvider = NotifierProvider<CounterNotifier, int>(CounterNotifier.new);
8.2 注入与消费
void main() {
runApp(
const ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends ConsumerWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(
child: Text('${ref.watch(counterProvider)}'),
),
floatingActionButton: FloatingActionButton(
onPressed: () => ref.read(counterProvider.notifier).increment(),
child: const Icon(Icons.add),
),
),
);
}
}
9. 场景二:异步用户资料
9.1 AsyncNotifier 实现
class UserProfileNotifier extends AsyncNotifier<UserProfile> {
@override
Future<UserProfile> build() async {
final api = ref.read(apiClientProvider);
return api.fetchProfile();
}
Future<void> refresh() async {
state = const AsyncLoading();
try {
final profile = await ref.read(apiClientProvider).fetchProfile();
if (!ref.mounted) return;
state = AsyncData(profile);
} catch (e, st) {
if (!ref.mounted) return;
state = AsyncError(e, st);
}
}
}
final userProfileProvider = AsyncNotifierProvider<UserProfileNotifier, UserProfile>(
UserProfileNotifier.new,
);
9.2 UI 消费
class ProfilePage extends ConsumerWidget {
const ProfilePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final profile = ref.watch(userProfileProvider);
return profile.when(
data: (data) => Column(
children: [
Text(data.name),
Text(data.email),
ElevatedButton(
onPressed: () => ref.read(userProfileProvider.notifier).refresh(),
child: const Text('刷新'),
),
],
),
loading: () => const Center(child: CircularProgressIndicator()),
error: (e, st) => Center(child: Text('错误: $e')),
);
}
}
10. 场景三:购物车与依赖组合
10.1 模型与多 Provider 依赖
class CartItem {
final String id;
final String name;
final double price;
final int quantity;
CartItem({required this.id, required this.name, required this.price, this.quantity = 1});
}
final cartProvider = NotifierProvider<CartNotifier, List<CartItem>>(CartNotifier.new);
final cartItemCountProvider = Provider<int>((ref) {
return ref.watch(cartProvider).fold<int>(0, (sum, item) => sum + item.quantity);
});
final cartTotalProvider = Provider<double>((ref) {
return ref.watch(cartProvider).fold<double>(
0,
(sum, item) => sum + item.price * item.quantity,
);
});
10.2 CartNotifier 实现
class CartNotifier extends Notifier<List<CartItem>> {
@override
List<CartItem> build() => [];
void add(CartItem item) {
state = [...state, item];
}
void remove(String id) {
state = state.where((i) => i.id != id).toList();
}
}
11. 场景四:family 参数化
11.1 按 ID 获取详情
final productDetailProvider = FutureProvider.autoDispose.family<Product, String>(
(ref, productId) async {
final api = ref.watch(apiClientProvider);
return api.fetchProduct(productId);
},
);
// 使用
class ProductDetailPage extends ConsumerWidget {
const ProductDetailPage({required this.productId, super.key});
final String productId;
@override
Widget build(BuildContext context, WidgetRef ref) {
final product = ref.watch(productDetailProvider(productId));
return product.when(
data: (p) => Text(p.name),
loading: () => const CircularProgressIndicator(),
error: (e, _) => Text('$e'),
);
}
}
12. 常见问题与最佳实践
12.1 initState 中访问 Provider
在 ConsumerStatefulWidget 的 ref 下使用 ref.read,且若会同步触发状态更新,用 Future.microtask 推迟:
@override
void initState() {
super.initState();
Future.microtask(() {
ref.read(userProfileProvider.notifier).refresh();
});
}
12.2 避免在 build 中用 ref.read 监听
需要监听时用 ref.watch,否则不会重建。
12.3 异步操作后检查 ref.mounted
在 AsyncNotifier 或 Notifier 的异步方法中,await 之后更新 state 前应检查 ref.mounted:
Future<void> loadData() async {
final data = await api.fetch();
if (!ref.mounted) return; // 已 dispose 则不再更新
state = AsyncData(data);
}
12.4 合理使用 autoDispose
- 页面级、列表项级:建议
autoDispose - 全局单例、用户信息:通常不用
autoDispose
12.5 项目结构建议
lib/
app/
app.dart
providers/ # 全局 Provider
features/
auth/
providers/
notifiers/
views/
cart/
providers/
notifiers/
views/
13. Riverpod 3.2.x 版本要点
13.1 3.2.1 修复
- 修复恢复暂停的 provider 后可能不再通知监听器的 bug
13.2 3.2.0 变更
- 修复
selectAsync取消订阅时可能抛错 - 修复
ref.mounted在 provider 重建后对陈旧 ref 仍返回true导致竞态 - 修复 Notifier 在依赖变化时丢失状态的回归
- 新增
Ref.isPaused检查是否有活跃监听者 family.overrideWith弃用,改用family.overrideWith2
13.3 3.x 与 2.x 主要差异
StateNotifierProvider、StateProvider移至legacy.dart,推荐使用NotifierProviderRef子类(如FutureProviderRef)移除,统一使用RefProvider.autoDispose()可写为Provider(isAutoDispose: true)- 所有 Provider 默认用
==比较值并过滤重复更新
14. 附录:快速参考
| 操作 | 代码 |
|---|---|
| 创建 Provider | Provider((ref) => value) |
| 创建 Notifier | NotifierProvider<X, T>(X.new) |
| 创建 AsyncNotifier | AsyncNotifierProvider<X, T>(X.new) |
| 监听并重建 | ref.watch(provider) |
| 只读不监听 | ref.read(provider) |
| 监听并副作用 | ref.listen(provider, (prev, next) {}) |
| 失效 | ref.invalidate(provider) |
| 立即刷新 | ref.refresh(provider) |
| 参数化 | Provider.family<T, Arg>((ref, arg) => ...) |
| 自动释放 | Provider.autoDispose((ref) => ...) |
| 根节点 | ProviderScope(child: MyApp()) |
参考资源
总结
整篇文档可以归纳成一句话:
Riverpod 是一套“Provider 即带缓存的函数 + 依赖图驱动自动失效”的响应式状态管理框架。
如果你的 Flutter 项目需要:
- 编译期安全:不依赖 BuildContext,可在任意处使用
- 异步优先:FutureProvider、AsyncNotifier、AsyncValue 原生支持
- 可测试性:override 任意 Provider,无需 Widget 树
- 自动缓存与失效:
ref.watch自动建立依赖,依赖变化自动重建 - 多实例同类型:同一类型可有多个 Provider
那么 Riverpod 非常推荐使用