目录
Riverpod 使用手册:Flutter 状态管理新范式
一、Riverpod 是什么?
二、核心概念速查表
三、环境配置:让项目支持 Riverpod
1. 添加依赖
2. 初始化 Riverpod
四、实战 1:用 StateProvider 实现计数器
需求:一个按钮,点击后数字 +1
步骤 1:定义 Provider
步骤 2:在 Widget 中使用 Provider
效果验证
五、实战 2:用 FutureProvider 实现异步请求
需求:加载网络数据并显示
步骤 1:定义 FutureProvider
步骤 2:在 Widget 中处理异步状态
效果验证
六、实战 3:Provider 依赖与组合
需求:根据用户 ID 动态加载用户信息
步骤 1:定义 “用户 ID” 的 Provider
步骤 2:让 userProvider 依赖 userIdProvider
步骤 3:动态修改用户 ID(进阶)
七、Riverpod vs 其他框架:怎么选?
八、最佳实践:这些坑别踩!
九、总结:Riverpod 值得学吗?
一、Riverpod 是什么?
在 Flutter 开发中,状态管理是绕不开的话题。Riverpod 作为新一代状态管理框架,以简洁、灵活、强类型为核心优势,逐渐成为开发者的新宠。它解决了 Provider 框架的一些痛点,让状态管理更可控,甚至能和 GetX、Bloc 分庭抗礼!
简单说,Riverpod 是 **“状态管理的瑞士军刀”**:
- 支持全局状态、局部状态
- 天然兼容 Dart 空安全和异步逻辑
- 写法简洁,学习曲线比 Bloc 平缓
接下来,咱们从 0 开始,手把手教你用 Riverpod 玩转状态管理!
二、核心概念速查表
先记住这些基础概念,后面会逐个拆解:
| 概念 | 作用 | 类比(Web 开发) |
|---|---|---|
Provider |
最基础的 “状态提供者”,返回固定值或简单计算结果 | React 的 useState
|
StateProvider |
支持修改状态的 Provider,适合简单可变状态(如开关、计数器) | Vue 的 data
|
FutureProvider |
处理异步操作(如网络请求),自动管理加载、成功、错误状态 | React Query 的 useQuery
|
StreamProvider |
处理流式数据(如实时消息、数据库监听) | RxJS 的 Observable
|
Consumer |
订阅 Provider 状态变化,触发 UI 重建 | React 的 useEffect
|
WidgetRef |
连接 Widget 和 Provider 的 “桥梁”,用于读取 / 更新状态 | Vue 的 this.$store
|
三、环境配置:让项目支持 Riverpod
1. 添加依赖
在 pubspec.yaml 中加入:
dependencies:
flutter_riverpod: ^2.4.0 # 核心库,必选
riverpod_annotation: ^2.1.0 # 注解支持(可选,简化代码)
build_runner: ^2.4.6 # 代码生成工具(配合注解时需要)
dev_dependencies:
riverpod_generator: ^2.2.0 # 注解生成器(配合注解时需要)
然后执行 flutter pub get 安装依赖。
2. 初始化 Riverpod
在 main.dart 中,用 ProviderScope 包裹根组件:
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(
// 关键:所有使用 Riverpod 的组件必须在 ProviderScope 内
const ProviderScope(
child: MyApp(),
),
);
}
ProviderScope 是 Riverpod 的 “状态容器”,负责管理所有 Provider 的生命周期。
四、实战 1:用 StateProvider 实现计数器
需求:一个按钮,点击后数字 +1
这是最基础的 “可变状态” 场景,用 StateProvider 就能轻松实现。
步骤 1:定义 Provider
新建 counter_provider.dart:
import 'package:flutter_riverpod/flutter_riverpod.dart';
// 定义一个 StateProvider,初始值为 0
final counterProvider = StateProvider<int>((ref) => 0);
-
StateProvider是 Riverpod 专门用于 **“简单可变状态”** 的工具。 -
ref是 “Provider 上下文”,可以用来读取其他 Provider(这里暂时用不到)。
步骤 2:在 Widget 中使用 Provider
在页面组件中,用 Consumer 订阅状态:
import 'package:flutter_riverpod/flutter_riverpod.dart';
class CounterPage extends ConsumerWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// 读取 counterProvider 的当前值
final count = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(title: const Text('Riverpod 计数器')),
body: Center(
child: Text(
'计数:$count',
style: const TextStyle(fontSize: 24),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 更新状态:读取当前值 +1
ref.read(counterProvider.notifier).state++;
},
child: const Icon(Icons.add),
),
);
}
}
-
ConsumerWidget:替代传统的StatelessWidget,让 Widget 能订阅 Provider。 -
ref.watch(provider):订阅状态,状态变化时会触发 Widget 重建。 -
ref.read(provider.notifier):获取状态的 “修改器”,用于更新值(类似setState)。
效果验证
运行代码后:
- 点击按钮,数字会 +1
- 热重载时状态不会丢失(因为状态由 Riverpod 管理,而非 Widget 自身)
五、实战 2:用 FutureProvider 实现异步请求
需求:加载网络数据并显示
比如从 GitHub API 获取用户信息,需要处理加载中、成功、失败三种状态。
步骤 1:定义 FutureProvider
新建 user_provider.dart:
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
// 模拟网络请求:根据用户名获取用户信息
Future<Map<String, dynamic>> fetchUser(String username) async {
final response = await http.get(
Uri.parse('https://api.github.***/users/$username'),
);
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw Exception('加载失败');
}
}
// 定义 FutureProvider,依赖一个用户名(这里固定为 'octocat')
final userProvider = FutureProvider<Map<String, dynamic>>((ref) {
return fetchUser('octocat'); // GitHub 官方示例用户
});
-
FutureProvider自动管理异步状态:-
loading:请求中,返回AsyncValue.loading() -
data:请求成功,返回AsyncValue.data(结果) -
error:请求失败,返回AsyncValue.error(异常)
-
步骤 2:在 Widget 中处理异步状态
import 'package:flutter_riverpod/flutter_riverpod.dart';
class UserPage extends ConsumerWidget {
const UserPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// 监听 userProvider 的状态变化
final userAsync = ref.watch(userProvider);
return Scaffold(
appBar: AppBar(title: const Text('GitHub 用户信息')),
body: Center(
child: userAsync.when(
loading: () => const CircularProgressIndicator(), // 加载中
error: (err, stack) => Text('错误:$err'), // 加载失败
data: (user) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.***work(
user['avatar_url'],
width: 100,
height: 100,
),
const SizedBox(height: 16),
Text(
'用户名:${user['login']}',
style: const TextStyle(fontSize: 18),
),
Text(
'ID:${user['id']}',
style: const TextStyle(fontSize: 16, color: Colors.grey),
),
],
), // 加载成功
),
),
);
}
}
-
AsyncValue.when:Riverpod 提供的便捷方法,按状态分支处理 UI,比手动判断connectionState简洁太多!
效果验证
- 首次进入页面:显示加载动画
- 请求成功:显示用户头像、用户名、ID
- 请求失败(断网时):显示错误提示
六、实战 3:Provider 依赖与组合
需求:根据用户 ID 动态加载用户信息
比如用户 ID 来自另一个 Provider,需要 “依赖注入”。
步骤 1:定义 “用户 ID” 的 Provider
final userIdProvider = Provider<String>((ref) => 'octocat');
这是一个只读 Provider,返回固定的用户 ID(实际项目中可能来自路由、全局配置等)。
步骤 2:让 userProvider 依赖 userIdProvider
修改 user_provider.dart:
final userProvider = FutureProvider<Map<String, dynamic>>((ref) {
// 读取 userIdProvider 的值
final userId = ref.watch(userIdProvider);
return fetchUser(userId);
});
-
ref.watch(otherProvider):在一个 Provider 中读取另一个 Provider 的值,实现依赖传递。 - 当
userIdProvider的值变化时,userProvider会自动重新执行请求。
步骤 3:动态修改用户 ID(进阶)
如果需要动态修改用户 ID,可以用 StateProvider:
final userIdProvider = StateProvider<String>((ref) => 'octocat');
// 在 Widget 中修改:
ref.read(userIdProvider.notifier).state = 'new_user';
这样,用户 ID 变化时,userProvider 会自动重新请求新的用户信息,UI 也会同步更新。
七、Riverpod vs 其他框架:怎么选?
| 框架 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Riverpod | 强类型、写法简洁、支持复杂依赖 | 学习曲线比 Provider 高 | 中大型项目、追求类型安全 |
| GetX | 功能全(状态 + 路由 + 依赖)、上手快 | 过度灵活可能导致代码混乱 | 小型项目、快速迭代 |
| Bloc | 严格的分层(UI-Bloc-State) | 样板代码多 | 大型项目、需要严格流程控制 |
简单总结:
- 追求类型安全、复杂状态依赖 → 选 Riverpod
- 追求极致开发速度、小而全 → 选 GetX
- 追求严格分层、团队规范 → 选 Bloc
八、最佳实践:这些坑别踩!
-
避免在 Provider 中写副作用逻辑
Provider 应该是 “纯函数”,只返回状态。副作用(如网络请求、数据库操作)尽量放在单独的服务类中。 -
合理使用
ref.watch和ref.read-
ref.watch:在 Widget 或 Provider 中订阅状态(状态变则重建) -
ref.read:在事件回调(如按钮点击)中一次性获取状态(不订阅)
-
-
用
family处理动态参数
如果 Provider 需要动态参数(如根据 ID 加载数据),可以用family:final userProvider = FutureProvider.family<Map, String>((ref, id) { return fetchUser(id); }); // 使用:ref.watch(userProvider('octocat')) -
记得清理资源
对于StreamProvider或自定义的异步 Provider,必要时用ref.onDispose清理资源:final streamProvider = StreamProvider((ref) { final stream = ...; ref.onDispose(() => stream.cancel()); // 组件销毁时取消流 return stream; });
九、总结:Riverpod 值得学吗?
非常值得! 它解决了 Flutter 状态管理的核心痛点:
- 类型安全:告别
dynamic地狱 - 依赖清晰:Provider 之间的依赖关系一目了然
- 异步友好:
FutureProvider/StreamProvider简化异步逻辑
如果你正在开发中大型 Flutter 项目,或者受够了 Provider 的一些限制,Riverpod 绝对能让你相见恨晚!