Flutter Provider 用法总结(含 ChangeNotifierProvider、Consumer、ChangeNotifierProxyProvider, BlocListener, BlocBuilder)
1. ChangeNotifierProvider
ChangeNotifierProvider<T>
是最常用的 Provider,作用是在 Widget 树中提供一个 T 类型(通常为 ChangeNotifier)的实例,供其子 Widget 访问和监听。- 用法示例:
dart
ChangeNotifierProvider<SkinSelectNotifier>(
create: (_) => SkinSelectNotifier(widget.vehicle),
child: ...
)
- 上面代码会用
widget.vehicle
创建一个新的SkinSelectNotifier
实例,并将其注入到子 Widget 树中。
2. Consumer
Consumer<T>
是 Provider 体系中用于监听并响应 T 类型 Provider 状态变化的 Widget。- 只要 Provider 中的数据有变化,
builder
就会自动重建。 - 用法示例:
dart
Consumer<SkinSelectNotifier>(
builder: (_, value, __) {
// value 就是最新的 SkinSelectNotifier 实例
// 只要 SkinSelectNotifier.notifyListeners 被调用,builder 就会刷新
}
)
3. ChangeNotifierProxyProvider
3.1 作用
ChangeNotifierProxyProvider<A, B>
用于创建一个 B 类型(ChangeNotifier),并让它可以依赖于另一个 Provider A 的变化。- 当 A 发生变化时,B 可以自动获得最新的 A 实例,及时更新自身状态。
3.2 create 和 update 的区别
create
:首次在 Widget 树中创建 B(ChangeNotifier)实例时调用。只执行一次。update
:每当 A(依赖的 Provider)发生变化,或 Provider 热重载/依赖变化时都会被调用。会多次执行。
3.3 推荐写法
dart
ChangeNotifierProxyProvider<MyModel, MyChangeNotifier>(
create: (_) => MyChangeNotifier(),
update: (_, myModel, myNotifier) => myNotifier..update(myModel),
)
- 解释 :
create
只负责实例化 MyChangeNotifier。update
保证每次 MyModel 变化时,将最新的 MyModel 注入 MyChangeNotifier,并复用旧实例。
⚠️ 注意事项
- 不要 在
update
内直接 new 出新的 B,否则之前的数据和状态会丢失。 - 推荐在 B 内部实现 update 方法,用于同步依赖变化。
4. 应用场景举例
- 如果你的 ChangeNotifier 需要依赖于别的 Provider(如用户信息、配置、全局事件流),优先使用
ChangeNotifierProxyProvider
。 - 如果只是需要一个简单的可变状态管理,直接用
ChangeNotifierProvider
即可。
5. BlocListener 的 listenWhen 与 BlocBuilder 的关系总结
5.1 listenWhen 是什么?什么时候触发?
listenWhen
是BlocListener
的一个可选回调函数,签名如下:
dart
bool listenWhen(previous, current)
- 每当 Bloc 的状态发生变化时,
listenWhen
会被调用一次,传入前一个状态previous
和当前状态current
。 - 若返回
true
,则会执行listener
回调。 - 若返回
false
,则不执行。
5.2 listenWhen 的用途
用于过滤监听条件,避免每次状态变化都触发副作用,只在关心的状态变化时执行。例如:
dart
listenWhen: (previous, current) {
final isActualLogin = previous.user == null && current.user != null;
final shouldListen = isActualLogin && (current.action is LoginAction);
return shouldListen || previous != current;
}
表示:
- 用户从未登录到已登录,并且 action 是 LoginAction 时触发
- 或者状态有变化时触发
5.3 返回 true / false 会怎样?
- 返回 true:执行
listener: (context, state) { ... }
- 返回 false:不会执行
listener
5.4 BlocListener 和 BlocBuilder 的区别与关系
对象 | 作用 | 是否重建 UI | 是否执行副作用 | 有条件过滤功能 |
---|---|---|---|---|
BlocListener | 执行副作用(如导航) | 否 | 是 | 使用 listenWhen |
BlocBuilder | 构建 UI | 是 | 否 | 使用 buildWhen |
BlocListener
不负责 UI 构建,只在特定状态变化时做出响应。BlocBuilder
始终对状态变化做出响应(除非使用buildWhen
过滤)。listenWhen
和buildWhen
可以搭配使用,实现精细控制。
总结
listenWhen
用于决定 BlocListener 的 listener 是否触发。- 返回 true 会执行副作用逻辑,false 不执行。
- BlocBuilder 总是响应 Bloc 状态变化,和 listenWhen 无关,若要过滤 UI 重建需使用 buildWhen。
- 二者搭配使用能实现副作用处理和 UI 构建的解耦。