简介
Provider 是 Flutter 官方推荐的状态管理方案,它基于 InheritedWidget 封装,提供了一种简单且可扩展的状态管理方式。
基本概念
ChangeNotifier
class Counter extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 通知监听器数据已更新
}
}
Provider 类型
// 基础 Provider
Provider<T>
// 可监听变化的 Provider
ChangeNotifierProvider<T>
// 多个 Provider 组合
MultiProvider
// 消费者组件
Consumer<T>
基本用法
创建 Provider
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
访问数据
// 方式1: Consumer
Consumer<Counter>(
builder: (context, counter, child) {
return Text('Count: ${counter.count}');
},
);
// 方式2: Provider.of
final counter = Provider.of<Counter>(context);
Text('Count: ${counter.count}');
// 方式3: context.read/watch
Text('Count: ${context.watch<Counter>().count}');
完整示例
// 数据模型
class TodoModel extends ChangeNotifier {
final List<Todo> _todos = [];
List<Todo> get todos => _todos;
void addTodo(Todo todo) {
_todos.add(todo);
notifyListeners();
}
void toggleTodo(int index) {
_todos[index].completed = !_todos[index].completed;
notifyListeners();
}
void removeTodo(int index) {
_todos.removeAt(index);
notifyListeners();
}
}
// 页面组件
class TodoListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Todo List')),
body: Consumer<TodoModel>(
builder: (context, todoModel, child) {
return ListView.builder(
itemCount: todoModel.todos.length,
itemBuilder: (context, index) {
final todo = todoModel.todos[index];
return ListTile(
leading: Checkbox(
value: todo.completed,
onChanged: (_) => todoModel.toggleTodo(index),
),
title: Text(todo.title),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => todoModel.removeTodo(index),
),
);
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 添加新任务
context.read<TodoModel>().addTodo(
Todo(title: 'New Task ${DateTime.now()}'),
);
},
child: Icon(Icons.add),
),
);
}
}
// 主应用
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => TodoModel()),
ChangeNotifierProvider(create: (_) => ThemeModel()),
],
child: Consumer<ThemeModel>(
builder: (context, themeModel, child) {
return MaterialApp(
theme: themeModel.currentTheme,
home: TodoListPage(),
);
},
),
);
}
}
高级用法
选择性重建
// 使用 Selector 只在特定值变化时重建
Selector<TodoModel, int>(
selector: (context, todoModel) => todoModel.todos.length,
builder: (context, todoCount, child) {
return Text('Todo count: $todoCount');
},
);
Provider 组合
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserModel()),
ProxyProvider<UserModel, CartModel>(
update: (context, userModel, previous) =>
CartModel(userModel.userId),
),
ChangeNotifierProxyProvider<UserModel, SettingsModel>(
create: (_) => SettingsModel(),
update: (context, userModel, settings) =>
settings!..updateUser(userModel),
),
],
child: MyApp(),
)
最佳实践
- 监听粒度用 Consumer/Selector:整页
context.watch一个 ChangeNotifier,任何字段一变整页 rebuild;只关心user.name就用Selector<UserModel, String>(selector: (_, u) => u.name),name 不变就不 rebuild - 回调里用 context.read,build 里用 context.watch:按钮 onPressed 里
context.read<Cart>().add(item);read 在 build 里拿不到更新,watch 在回调里用会抛异常——initState 里只能 read,不能 watch - 列表 item 的 provider 用
.value构造:ListView.builder 里写ChangeNotifierProvider.value(value: items[i]);用 create 会在 item 复用时错乱、重复 dispose - ChangeNotifier 持有的 StreamSubscription/Timer,在 dispose() 里取消:页面 pop 了回调还在跑,notifyListeners 触发已 dispose 的监听直接抛异常
注意事项
- 绝不在 build 方法里调 notifyListeners:build → notify → 标 dirty → 又 build,死循环,页面直接卡死——这是 Provider 新手第一坑
- ChangeNotifier 别塞太多字段:一个 model 管 20 个字段,改一个字段全量通知所有监听者;按”一起变”拆 model(UserModel、CartModel 分开)
- 只有跨页面共享的状态才放顶层 Provider:页面内部状态用 StatefulWidget 的 setState 就够了,别为了”统一”全上 Provider
- 异步操作里 notify 前先确认还活着:await 之后页面可能已经 pop 了,调 notify 前检查
hasListeners,或者包 try-catch
实战案例
Provider 我早期用过,后来全切到 GetX。切换的直接导火索是 context 依赖:Provider 的 context.read/watch 必须在 widget 树里有 context,但在 controller/service 层(比如网络回调里要更新全局用户状态)拿不到 context,得层层传,非常别扭。GetX 的 Get.find<UserController>() 哪都能调。当然 Provider 也有优点:它是官方推荐,心智模型简单,和 ChangeNotifier 配合学习成本低。如果是给新手团队或 Demo 项目,我反而推荐 Provider——选 GetX 是因为我熟,不是因为它”更好”。状态管理选型,团队熟悉度权重应该占一半。
总结
Provider 提供了一种简单且强大的状态管理方案,通过合理使用可以有效管理应用状态。理解并掌握 Provider 的使用对于开发高质量的 Flutter 应用至关重要。
