Flutter 中的 Provider 状态管理库,它是基于 InheritedWidget 封装的轻量级、易上手的状态管理方案,也是 Flutter 官方推荐的主流方案之一。我会从「核心概念、基本用法、进阶场景、性能优化、和原生 InheritedWidget 的对比」几个维度,由浅入深讲解,让你既能快速上手,也能理解底层逻辑。
一、Provider 是什么?
Provider 是 Flutter 生态中最流行的状态管理库之一,核心定位是:
- 封装 InheritedWidget :解决原生
InheritedWidget代码冗余、手动管理依赖的痛点; - 响应式状态管理:状态变化时,仅依赖该状态的 Widget 自动重建;
- 轻量易用:无需复杂的设计模式(如 Bloc),新手也能快速掌握;
- 单向数据流:状态变更逻辑集中管理,便于调试和维护。
核心优势(对比原生 InheritedWidget)
| 特性 | 原生 InheritedWidget | Provider |
|---|---|---|
| 代码量 | 多(需自定义子类、管理依赖) | 少(一行代码封装状态) |
| 状态更新 | 需手动重建 InheritedWidget | 自动通知依赖 Widget |
| 多状态管理 | 需嵌套多个 InheritedWidget | 支持多 Provider 组合 |
| 状态复用 | 差 | 好(可跨页面共享) |
二、Provider 核心概念
在使用 Provider 前,先理解 3 个核心类:
| 类名 | 作用 |
|---|---|
ChangeNotifier |
状态载体:存储可变化的状态,提供 notifyListeners() 方法通知状态变更 |
ChangeNotifierProvider |
状态提供者:将 ChangeNotifier 注入 Widget 树,供子 Widget 获取 |
Consumer/Provider.of() |
状态消费者:子 Widget 中获取状态,建立依赖绑定 |
三、Provider 基础使用步骤(以「计数器」为例)
步骤 1:添加依赖
yaml
dependencies:
flutter:
sdk: flutter
provider: ^6.1.5+1 # 查看 pub.dev 获取最新版本
步骤 2:定义状态类(继承 ChangeNotifier)
这是存储状态的核心,所有可变化的状态都放在这里,状态变更时调用 notifyListeners() 通知消费者:
csharp
import 'package:flutter/foundation.dart';
// 计数器状态类
class CounterProvider extends ChangeNotifier {
// 可变化的状态
int _count = 0;
// 对外暴露的只读属性(避免外部直接修改状态)
int get count => _count;
// 状态变更方法(集中管理逻辑)
void increment() {
_count++;
// 通知所有依赖的 Widget 状态变更,触发重建
notifyListeners();
}
void decrement() {
_count--;
notifyListeners();
}
void reset() {
_count = 0;
notifyListeners();
}
}
步骤 3:注入 Provider 到 Widget 树
通过 ChangeNotifierProvider 将状态类注入 Widget 树,子树中所有 Widget 都能获取该状态:
typescript
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
// 根节点注入 Provider(整个 App 可共享该状态)
ChangeNotifierProvider(
// 创建状态实例
create: (context) => CounterProvider(),
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Provider 示例',
home: const CounterPage(),
);
}
}
步骤 4:消费状态(3 种方式)
方式 1:Provider.of(context)(基础)
最直接的方式,获取状态并建立依赖绑定:
less
class CounterPage extends StatelessWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context) {
// 获取状态实例(listen: true 表示监听状态变化,默认 true)
final counter = Provider.of<CounterProvider>(context);
return Scaffold(
appBar: AppBar(title: const Text('Provider 计数器')),
body: Center(
child: Text(
'计数:${counter.count}',
style: const TextStyle(fontSize: 24),
),
),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: counter.decrement, // 调用状态方法
child: const Icon(Icons.remove),
),
const SizedBox(width: 10),
FloatingActionButton(
onPressed: counter.increment,
child: const Icon(Icons.add),
),
],
),
);
}
}
方式 2:Consumer(推荐,精准重建)
Consumer 可以精准控制重建范围,避免整个页面重建(性能更优):
less
class CounterPage extends StatelessWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context) {
print('CounterPage 整体重建了吗?'); // 状态变化时,这里不会打印!
return Scaffold(
appBar: AppBar(title: const Text('Consumer 示例')),
body: Center(
// 仅 Consumer 包裹的部分会重建
child: Consumer<CounterProvider>(
builder: (context, counter, child) {
print('Consumer 内部重建了'); // 状态变化时,这里会打印
return Text(
'计数:${counter.count}',
style: const TextStyle(fontSize: 24),
);
},
),
),
floatingActionButton: Consumer<CounterProvider>(
builder: (context, counter, child) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: counter.decrement,
child: const Icon(Icons.remove),
),
const SizedBox(width: 10),
FloatingActionButton(
onPressed: counter.increment,
child: const Icon(Icons.add),
),
],
);
},
),
);
}
}
方式 3:Selector(更精准,过滤重建)
Selector 可以指定「监听的状态属性」,只有该属性变化时才重建(性能最优):
php
// 示例:仅当 count 为偶数时才重建
child: Selector<CounterProvider, bool>(
// 选择要监听的属性(count 是否为偶数)
selector: (context, counter) => counter.count % 2 == 0,
builder: (context, isEven, child) {
return Text(
'计数:${Provider.of<CounterProvider>(context).count}\n是否偶数:$isEven',
style: const TextStyle(fontSize: 24),
textAlign: TextAlign.center,
);
},
),
四、Provider 进阶用法
1. 多状态管理(MultiProvider)
当需要注入多个状态类时,用 MultiProvider 避免嵌套:
scss
// 定义第二个状态类(用户信息)
class UserProvider extends ChangeNotifier {
String _userName = '张三';
String get userName => _userName;
void updateName(String name) {
_userName = name;
notifyListeners();
}
}
// 注入多个 Provider
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => CounterProvider()),
ChangeNotifierProvider(create: (context) => UserProvider()),
],
child: const MyApp(),
),
);
}
// 消费多个状态
class MultiStatePage extends StatelessWidget {
const MultiStatePage({super.key});
@override
Widget build(BuildContext context) {
final counter = Provider.of<CounterProvider>(context);
final user = Provider.of<UserProvider>(context);
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('计数:${counter.count}'),
Text('用户名:${user.userName}'),
ElevatedButton(
onPressed: () => user.updateName('李四'),
child: const Text('修改用户名'),
),
],
),
),
);
}
}
2. 局部状态管理(页面内共享)
若状态仅在某个页面内共享,只需在该页面的 Widget 树中注入 Provider:
scala
class LocalStatePage extends StatelessWidget {
const LocalStatePage({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => CounterProvider(), // 仅该页面可用
child: Scaffold(
appBar: AppBar(title: const Text('局部状态')),
body: const LocalCounterWidget(),
),
);
}
}
// 子 Widget 获取局部状态
class LocalCounterWidget extends StatelessWidget {
const LocalCounterWidget({super.key});
@override
Widget build(BuildContext context) {
final counter = Provider.of<CounterProvider>(context);
return Text('局部计数:${counter.count}');
}
}
五、性能优化技巧
-
缩小重建范围 :优先使用
Consumer/Selector,避免用Provider.of导致整个 Widget 重建; -
避免不必要的 notifyListeners () :仅状态真的变化时调用(如判断
_count变化后再调用); -
使用 lazy 初始化 :
ChangeNotifierProvider的lazy: true(默认),仅当首次消费时才创建状态实例; -
dispose 释放资源 :若状态类持有网络 / 定时器等资源,需重写
dispose:scalaclass TimerProvider extends ChangeNotifier { late Timer _timer; int _seconds = 0; TimerProvider() { _timer = Timer.periodic(const Duration(seconds: 1), (timer) { _seconds++; notifyListeners(); }); } // 释放定时器资源 @override void dispose() { _timer.cancel(); super.dispose(); } } -
避免在 build 中创建状态 :始终在
create中创建,而非build方法(否则会重复创建)。
六、Provider 常见坑点
-
context 范围问题 :在注入 Provider 的同一层级,无法直接用
Provider.of获取状态(需用Builder包裹);dart
less// 错误示例:context 是 MyApp 的 context,无法获取 Provider ChangeNotifierProvider( create: (context) => CounterProvider(), child: Text('${Provider.of<CounterProvider>(context).count}'), // 报错 ); // 正确示例:用 Builder 切换 context ChangeNotifierProvider( create: (context) => CounterProvider(), child: Builder( builder: (context) { return Text('${Provider.of<CounterProvider>(context).count}'); }, ), ); -
listen: false 慎用 :
Provider.of<T>(context, listen: false)仅获取状态,不建立依赖,状态变化时不会重建; -
多 Provider 命名冲突 :若有多个同类型 Provider,需用
ProviderScope或Consumer的selector区分。
总结
- 核心定位 :Provider 是
InheritedWidget的优雅封装,主打「轻量、易用、响应式」,适合中小规模 App 的状态管理; - 核心流程 :定义
ChangeNotifier状态类 → 用ChangeNotifierProvider注入 → 用Consumer/Selector消费; - 性能关键 :缩小重建范围(Consumer/Selector)、避免不必要的
notifyListeners()、及时释放资源; - 适用场景:全局状态(用户信息、主题)、页面内局部状态(表单数据、计数器),不适合超复杂的状态逻辑(可换 Bloc/Riverpod)。