HookConsumerWidget 深入理解
HookConsumerWidget` 是 Flutter Hooks + Riverpod 的组合,实际是:
HookConsumerWidget ≈ HookWidget + ConsumerWidget
- HookWidget:提供局部状态管理能力(useState, useEffect 等)
- ConsumerWidget:提供全局状态访问能力(ref.watch, ref.read, ref.listen)
构建原理:
build(context, ref)├─ useHook(...) → 局部状态管理└─ ref.watch(provider) → 全局状态监听
特点:
- 局部状态仅在当前 Widget 生命周期内存在
- 全局状态可跨 Widget、跨页面共享
- 局部 + 全局状态可以同时使用,代码更简洁
1️⃣ 局部状态(Hook)全面解析
局部状态是 Widget 私有状态,通过 Hook 管理:
2.1 常用 Hook
Hook | 用途 | 例子 |
---|---|---|
useState<T>() | 存储简单值,类似 setState | final counter = useState(0); |
useEffect() | 生命周期副作用 | useEffect(() { return cleanup; }, [deps]); |
useMemoized() | 缓存对象,避免重复创建 | final obj = useMemoized(() => ExpensiveObj()); |
useTextEditingController() | 输入框控制器 | final controller = useTextEditingController(); |
useScrollController() | 滚动控制器 | final scrollController = useScrollController(); |
useAnimationController() | 动画控制器 | final animController = useAnimationController(); |
2.2 局部状态使用原则
- 只存放当前 Widget 的 UI 状态
- 生命周期和 Widget 一致
- 不跨 Widget 使用
- 可以和全局状态组合使用
2.3 局部状态示例
class LocalCounterWidget extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) {final localCount = useState(0);return Column(children: [Text('局部计数: ${localCount.value}'),ElevatedButton(onPressed: () => localCount.value++,child: Text('局部+1'),),],);}
}
2️⃣ 全局状态(Riverpod)全面解析
全局状态是 跨 Widget 可共享状态,由 Provider 管理。
3.1 Provider 类型
Provider | 用途 | 特点 |
---|---|---|
Provider | 只读值 | 简单数据 |
StateProvider | 可变值 | 轻量级状态管理 |
StateNotifierProvider | 复杂状态 | 封装逻辑,推荐用法 |
FutureProvider | 异步数据 | 自动处理 loading/error |
StreamProvider | 流数据 | 自动监听 stream |
3.2 使用示例
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {return CounterNotifier();
});class CounterNotifier extends StateNotifier<int> {CounterNotifier() : super(0);void increment() => state++;void decrement() => state--;
}
在 HookConsumerWidget 中访问:
build(BuildContext context, WidgetRef ref) {final globalCount = ref.watch(counterProvider);return Column(children: [Text('全局计数: $globalCount'),ElevatedButton(onPressed: () => ref.read(counterProvider.notifier).increment(),child: Text('全局+1'),),],);
}
Widget
3️⃣ 局部 + 全局状态组合
最佳实践:
- UI 局部状态 → Hook (
useState
) - 业务或共享状态 → Riverpod Provider
示例:
class CombinedCounterWidget extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) {final localCount = useState(0);final globalCount = ref.watch(counterProvider);return Column(children: [Text('局部: ${localCount.value}'),Text('全局: $globalCount'),ElevatedButton(onPressed: () => localCount.value++,child: Text('局部+1'),),ElevatedButton(onPressed: () => ref.read(counterProvider.notifier).increment(),child: Text('全局+1'),),],);}
}
🔹 局部状态只影响当前 Widget
🔹 全局状态会通知所有依赖它的 Widget 重建
4️⃣ 全局状态监听与副作用
5.1 ref.listen
ref.listen<int>(counterProvider, (previous, next) {if (next >= 10) {ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('计数到10!')));}
});
特点:
- 不触发 Widget rebuild
- 常用于副作用处理:弹窗、路由跳转、日志记录
5.2 ref.listenManual
与 ref.watch
watch
→ rebuild Widgetlisten
→ 不 rebuild,用于副作用
5️⃣ 生命周期对比
类型 | 生命周期 | 构建重建 | 清理资源 |
---|---|---|---|
Hook 局部状态 | Widget 生命周期 | rebuild 时更新 | useEffect 返回 dispose |
Riverpod 全局状态 | Provider 生命周期 | watch 时 rebuild | Provider dispose 自动 |
HookConsumerWidget | 结合两者 | rebuild 根据 watch 或 Hook 更新 | Hook dispose + Provider dispose |
6️⃣ 性能优化技巧
- 局部状态避免全局依赖
final count = useState(0); // 局部
- Provider 切分粒度
- 小的 Provider → 减少 rebuild
- 大的 Provider → rebuild 范围大
- 使用
ref.read
避免不必要 rebuild - 缓存对象
final obj = useMemoized(() => ExpensiveObject());
- 动画或控制器使用 Hook
final animController = useAnimationController(); useEffect(() => animController.dispose, []);
7️⃣ 局部 + 全局状态逻辑图(文字版)
HookConsumerWidget├─ 局部状态(useState/useEffect等)│ └─ 生命周期: Widget 生命周期│ └─ rebuild: 局部状态变化└─ 全局状态(Provider/StateNotifier)├─ ref.watch → rebuild Widget├─ ref.read → 只读,不 rebuild└─ ref.listen → 副作用处理,不 rebuild
8️⃣ 实战场景举例
场景 | 局部状态 | 全局状态 |
---|---|---|
输入框 + 提交按钮 | TextEditingController | 提交后的全局数据 |
游戏分数显示 | 动画进度 | 全局分数 |
购物车 UI 切换 | TabIndex | 全局购物车列表 |
总结
- HookConsumerWidget = HookWidget + ConsumerWidget
- 局部状态管理 UI 临时状态
- 全局状态管理跨 Widget 共享数据
- 性能优化需注意 rebuild 范围、对象缓存和副作用处理
- 结合使用可以使 Flutter 代码更简洁、更易维护