1. 背景 & 起源
- Provider 的局限
- 依赖
BuildContext
,在一些场景下(比如异步回调、业务逻辑层)取值不方便。 - 热重载、热重启时容易丢失状态。
- 嵌套 Provider 容易写出 "Provider Hell"。
- 依赖
- Riverpod 的设计目标
- 彻底去掉对
BuildContext
的依赖。 - 支持 全局依赖管理,即在 UI 之外(比如 service 层)也能读写 Provider。
- 类型安全(不同 Provider 类型不会被误用)。
- 更优雅地支持异步流。
- 彻底去掉对
2. hooks_riverpod 是什么?
- riverpod:状态管理框架,核心思想是 Provider 作为 "依赖注入 + 状态容器"。
- flutter_hooks :提供类似 React Hooks 的 API(
useState
、useEffect
、useMemoized
),简化 UI 状态逻辑。 - hooks_riverpod:结合两者,让 Riverpod 的 Provider 与 Hook API 无缝配合,UI 写法更简洁。
👉 换句话说:
- riverpod 管理应用级状态。
- hooks 管理组件内部状态。
- hooks_riverpod = 把二者融合。
3. Provider 类型(完全版)
在 Riverpod 里,所有状态都是 Provider,不同 Provider 适用于不同业务场景。
Provider 类型 | 说明 | 示例场景 |
---|---|---|
Provider |
只读对象 | 全局配置、Service 单例 |
StateProvider<T> |
简单可变状态(值类型) | 计数器、开关、选中项 |
StateNotifierProvider<TNotifier, T> |
推荐,复杂状态管理(基于不可变数据模型) | 用户信息、购物车、业务状态机 |
NotifierProvider<TNotifier, T> |
Riverpod 2.x 的新版本,比 StateNotifier 更简洁 | 同上 |
FutureProvider<T> |
异步加载数据 | 网络请求、数据库读取 |
StreamProvider<T> |
响应式流 | WebSocket、消息订阅、Firebase |
ChangeNotifierProvider<T> |
兼容旧架构(ChangeNotifier) | 老项目迁移 |
AutoDispose 系列 |
自动释放 Provider(减少内存占用) | 页面退出时自动清理缓存 |
4. 核心概念
4.1 ProviderScope
- Riverpod 应用的根容器,类似
InheritedWidget
。 - 必须放在
runApp
的最外层:
dart
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
4.2 ref 对象
- 在 ConsumerWidget / HookConsumerWidget 中,通过
ref
访问 Provider。 - 三个常用方法:
ref.watch(provider)
→ 订阅(UI 会跟随更新)ref.read(provider)
→ 只读取一次(不会更新)ref.listen(provider, (prev, next) {})
→ 监听变化(副作用)
5. 基础用法
5.1 StateProvider
dart
final counterProvider = StateProvider<int>((ref) => 0);
class CounterPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Column(
children: [
Text('Count: $count'),
ElevatedButton(
onPressed: () => ref.read(counterProvider.notifier).state++,
child: Text('Increment'),
),
],
);
}
}
5.2 StateNotifierProvider(中大型推荐)
dart
// 定义状态管理器
class CounterNotifier extends StateNotifier<int> {
CounterNotifier(): super(0);
void increment() => state++;
void decrement() => state--;
}
// 定义 Provider
final counterProvider = StateNotifierProvider<CounterNotifier, int>(
(ref) => CounterNotifier(),
);
// 使用
class CounterPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
final notifier = ref.read(counterProvider.notifier);
return Row(
children: [
Text('$count'),
IconButton(icon: Icon(Icons.add), onPressed: notifier.increment),
],
);
}
}
5.3 FutureProvider(异步)
dart
final userProvider = FutureProvider<User>((ref) async {
final repo = ref.watch(userRepositoryProvider);
return repo.fetchUser();
});
class UserPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final asyncUser = ref.watch(userProvider);
return asyncUser.when(
data: (user) => Text('Hello ${user.name}'),
loading: () => CircularProgressIndicator(),
error: (err, _) => Text('Error: $err'),
);
}
}
5.4 Hooks 示例
dart
class TimerPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final counter = useState(0);
useEffect(() {
final timer = Timer.periodic(Duration(seconds: 1), (_) {
counter.value++;
});
return timer.cancel;
}, []);
return Text('Timer: ${counter.value}');
}
}
👉 useEffect
类似 React 的生命周期:挂载时执行回调,卸载时清理。
6. 底层原理(简要)
- ProviderScope 内部实现了一个 容器(Container),存储所有 Provider 的实例。
- 每个 Provider 都有依赖图(dependency graph),自动管理依赖更新。
- 当依赖的 Provider 更新时,Riverpod 会触发重新构建(只影响用到的 Widget,而不是整个树)。
- 这比 InheritedWidget / Provider 更智能,避免了不必要的 rebuild。
7. 最佳实践
- 状态分层
- UI 状态:
useState
/StateProvider
- 业务状态:
StateNotifierProvider
/NotifierProvider
- 异步状态:
FutureProvider
/StreamProvider
- UI 状态:
- 依赖注入
- Service、Repository、APIClient 用
Provider
- Service、Repository、APIClient 用
- 状态不可变
- 使用
copyWith
更新 state,保持不可变性
- 使用
- AsyncValue 统一管理异步
- Loading/Error/Data 状态三合一
- AutoDispose
- 页面级状态,用
autoDispose
,防止内存泄漏
- 页面级状态,用
8. 实战项目架构建议
text
lib/
├── main.dart
├── core/
│ ├── api/ (网络请求封装)
│ ├── repository/ (仓库层)
├── features/
│ ├── auth/ (登录模块)
│ │ ├── auth_provider.dart
│ │ ├── auth_notifier.dart
│ │ ├── auth_page.dart
│ ├── home/
│ ├── home_provider.dart
│ ├── home_page.dart
├── common/
├── widgets/
├── utils/
- 每个模块(auth/home/setting)都用 StateNotifierProvider 管理状态。
- 全局 Service(API、数据库)用
Provider
注入。 - UI 组件用
HookConsumerWidget
。
9. 优缺点对比
✅ 优点
- 无 Context 依赖
- 类型安全
- 异步优雅(AsyncValue)
- 支持依赖图(Provider 可以依赖 Provider)
- 结合 hooks 更简洁
❌ 缺点
- 学习曲线比 Provider 高
- 需要适应 "声明式状态" 思维
- 初学者容易滥用
ref.watch
导致 rebuild 过多