Provider 是 Flutter 官方推荐的状态管理方案之一。它的核心原理是依赖 InheritedWidget 实现数据在组件树中的高效向下传递,同时结合观察者模式,让数据变化时,只有依赖该数据的 UI 部分才会自动更新,从而实现了性能和开发体验的平衡
核心原理
Provider 本质是对 Flutter 底层 InheritedWidget 的封装。
- 数据传递 :
Provider组件将其持有的数据存储在InheritedWidget中,这使得组件树下任何一个子组件都能通过context访问到这份数据,无需手动层层传递 - 响应式更新 :当数据模型(通常混入
ChangeNotifier)发生变化时,它会调用notifyListeners()方法发出通知。Provider 监听到通知后,会强制刷新依赖该数据的Consumer或Selector等组件,从而实现 UI 的自动更新
核心组件介绍
要上手 Provider,主要就是熟悉下面这三个角色:
| 组件 | 类型 | 作用 |
|---|---|---|
ChangeNotifier |
数据模型 (Model) | 一个简单的类,来自 Flutter 原生 SDK。你通常需要 with ChangeNotifier 来为你的数据模型混入"通知"能力。当数据改变时,调用 notifyListeners()通知 |
ChangeNotifierProvider |
提供者 (Provider) | 顶级或上游的 Widget。它的 create 方法创建并持有数据模型的实例,并将其提供给整个子树 |
Consumer |
消费者 (Consumer) | 下游的 Widget。它声明自己需要监听哪个类型的数据模型,当该模型调用 notifyListeners() 时,Consumer 的 builder 方法会被执行,只重建自己这部分 UI |
基础用法四步走
接下来,用最经典的计数器示例,带你走完完整流程。
第 1 步:添加依赖
在你的 pubspec.yaml 文件中添加 provider 依赖,然后运行 flutter pub get。
yaml
dependencies:
flutter:
sdk: flutter
provider: ^# 6.1.5+1 # 建议使用最新版本
(最新版本号请以 pub.dev 为准)
第 2 步:创建数据模型(Model)
创建一个类来管理状态,并使用 ChangeNotifier。这是唯一需要你编写业务逻辑的地方
dart
scala
import 'package:flutter/material.dart';
// 1. 混入 (with) ChangeNotifier
class Counter with ChangeNotifier {
int _count = 0;
// 提供一个 getter 让外部获取状态,但无法直接修改
int get count => _count;
// 提供修改状态的方法
void increment() {
_count++;
// 2. 数据改变后,调用 notifyListeners() 通知所有监听者
notifyListeners();
}
}
第 3 步:通过 Provider 提供数据(Provide)
在应用或页面的顶层,使用 ChangeNotifierProvider 包裹你的根组件,将数据模型"注入"到组件树中
scala
// main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
// 将 ChangeNotifierProvider 置于顶层
ChangeNotifierProvider(
// create 方法负责创建模型实例
create: (context) => Counter(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
第 4 步:在组件中消费数据(Consume)
在需要访问数据和触发更新的子组件中,使用 Consumer 或 Provider.of 来获取模型实例
less
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Provider Demo')),
body: Center(
// 使用 Consumer 来监听 Counter 的变化
// 泛型 <Counter> 指明了要监听的模型类型
child: Consumer<Counter>(
builder: (context, counter, child) {
// builder 参数:context, 模型实例, 和可选的子widget
return Text(
'You have pushed the button this many times: ${counter.count}',
style: Theme.of(context).textTheme.headline4,
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 获取模型实例,listen: false 表示只调用方法,不监听变化
final counter = Provider.of<Counter>(context, listen: false);
counter.increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
💡 进阶用法与最佳实践
-
ConsumervsProvider.ofConsumer:推荐在build方法中使用。它能精确控制重建的范围,避免不必要的 UI 刷新Provider.of<T>(context, listen: false):仅用于触发方法 ,比如按钮的onPressed回调中。设置listen: false可以避免因模型重建而触发该 Widget 重建。Provider.of<T>(context)(等同于context.watch<T>()):会监听变化,应谨慎使用,避免导致父组件大范围重建。
-
管理多个 Provider
当需要管理多个数据模型时,使用
MultiProvider可以让代码结构更清晰scssMultiProvider( providers: [ ChangeNotifierProvider(create: (context) => CartModel()), ChangeNotifierProvider(create: (context) => UserModel()), Provider(create: (context) => SomeService()), // 其他不需要通知的服务 ], child: MyApp(), ) -
性能优化:
Selector如果模型中有多个属性,而某个 Widget 只关心其中一个,使用
Selector可以做到按需刷新,进一步提升性能dartSelector<Counter, int>( selector: (context, counter) => counter.count, // 只取出 count builder: (context, count, child) { return Text('$count'); }, ) -
处理依赖关系:
ProxyProvider当一个 Model 需要依赖另一个 Model 时,比如
UserModel依赖SettingsModel,可以使用ProxyProvider来解决
⚠️ 注意事项与常见问题
- 获取不到 Provider? :使用
Provider.of<T>(context)时,确保当前context的祖先节点中存在ChangeNotifierProvider<T>。通常把 Provider 放在MaterialApp之上即可。 - 忘记调用
notifyListeners():这是最常见的 Bug。修改完数据模型的属性后,务必 调用notifyListeners(),否则 UI 不会更新。 build方法频繁执行 :检查是否在build方法中直接使用了Provider.of<T>(context)而没有设置listen: false,或者Consumer放置的位置太高,重建范围过大。