一. 简介
在 Flutter 中,Provider 是一个非常流行且官方推荐的状态管理方案。它通过依赖注入(Dependency Injection)和响应式机制,让 UI 与数据状态解耦,实现“数据改变 → UI 自动刷新”的逻辑。
- 基于 观察者模式 实现。
- 它通过 ChangeNotifier 让数据变化自动通知订阅者,解耦 UI 和数据逻辑。
适用场景
- 单页面状态管理
- 跨组件/跨路由状态共享
- 简单全局状态(主题、语言、计数器等)
核心类介绍
- ChangeNotifier:一个实现了观察者模式的类,当状态发生变化时,调用’notifyListeners()’方法通知所有监听者更新
- ChangeNotifierProvider:一个 Widget,用于在 Widget 树中提供’ChangeNotifier’实例,使其子树中的 Widget 可以访问该实例
- Consumer:用于在子 Widget 中获取并监听’ChangeNotifier’实例,当状态变化时会重建自身
- Provider.of:另一种获取’ChangeNotifier’实例的方法,可以选择是否监听状态变化
二. 基本使用流程
使用 Provider 管理状态通常遵循以下步骤:
- 创建一个继承自
ChangeNotifier 的状态类,封装需要共享的状态和修改状态的方法
- 使用
ChangeNotifierProvider 在 Widget 树的适当位置提供该状态实例
- 在需要使用状态的子 Widget 中,通过
Consumer 或 Provider.of 获取状态并使用
下面通过一个简单的计数器示例来演示基本用法:
1. 创建状态类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import 'package:flutter/foundation.dart';
import 'package:flutter/foundation.dart';
class Counter extends ChangeNotifier { int _count = 0;
int get count => _count;
void increment() { _count++; notifyListeners(); }
void decrement() { _count--; notifyListeners(); }
void reset() { _count = 0; notifyListeners(); } }
|
2. 提供状态
在 Widget 树中使用 ChangeNotifierProvider 提供状态,通常放在应用的根部:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import 'package:provider/provider.dart';
void main() { runApp( ChangeNotifierProvider( create: (context) => Counter(), child: const MyApp(), ), ); }
class MyApp extends StatelessWidget { const MyApp({super.key});
@override Widget build(BuildContext context) { return MaterialApp( title: 'Provider Demo', home: const CounterPage(), ); } }
|
3. 消费状态
在子 Widget 中通过 Consumer 或 Provider.of 获取并使用状态:
使用 Consumer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import 'package:provider/provider.dart';
class CounterPage extends StatelessWidget { const CounterPage({super.key});
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Counter Demo')), body: Center( child: Consumer<Counter>( builder: (context, counter, child) { return Text( 'Current count: ${counter.count}', style: const TextStyle(fontSize: 24), ); }, ), ), floatingActionButton: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton( onPressed: () { Provider.of<Counter>(context, listen: false).decrement(); }, child: const Icon(Icons.remove), ), const SizedBox(width: 10), FloatingActionButton( onPressed: () { Provider.of<Counter>(context, listen: false).increment(); }, child: const Icon(Icons.add), ), ], ), ); } }
|
Consumer 性能优化
Consumer 的 child 参数可以用于优化性能,避免不必要的重建:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Consumer<Counter>( builder: (context, counter, child) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Count: ${counter.count}'), child!, ], ); }, child: const Text('This is a static text'), )
|
使用 Provider.of
Provider.of<T>(context) 会获取最近的 T 类型的 Provider,并在状态变化时重建当前 Widget:
1 2 3 4 5
| final counter = Provider.of<Counter>(context);
final counter = Provider.of<Counter>(context, listen: false);
|
使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class CounterDisplay extends StatelessWidget { const CounterDisplay({super.key});
@override Widget build(BuildContext context) { final counter = Provider.of<Counter>(context);
return Text( 'Count: ${counter.count}', style: const TextStyle(fontSize: 24), ); } }
class CounterActions extends StatelessWidget { const CounterActions({super.key});
@override Widget build(BuildContext context) { return FloatingActionButton( onPressed: () { Provider.of<Counter>(context, listen: false).increment(); }, child: const Icon(Icons.add), ); } }
|
4. 多个状态管理
当应用中有多个独立的状态需要管理时,可以使用 MultiProvider 来组织多个 Provider:
1 2 3 4 5 6 7 8 9 10 11 12
| void main() { runApp( MultiProvider( providers: [ ChangeNotifierProvider(create: (context) => Counter()), ChangeNotifierProvider(create: (context) => ThemeProvider()), ChangeNotifierProvider(create: (context) => UserProvider()), ], child: const MyApp(), ), ); }
|
这样,在子组件中可以分别获取不同的状态:
1 2 3 4 5
| final counter = Provider.of<Counter>(context);
final themeProvider = Provider.of<ThemeProvider>(context);
|
三. 实现计数器状态管理
在 pubspec.yaml 中添加依赖
1 2 3 4
| dependencies: flutter: sdk: flutter provider: ^6.0.5
|
^6.0.5 表示使用 ‘6.0.5 及以上的小版本更新‘。
- 根据项目需求,也可以用最新版本,如
provider: ^6.1.0。
获取依赖
在终端执行:
创建状态类(Model)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart';
class CounterModel with ChangeNotifier { int _count; CounterModel(this._count);
void add() { _count++; notifyListeners(); }
get count => _count; }
|
注入全局状态main.dart
1 2 3 4 5 6 7 8 9 10 11 12 13
| import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:provider_demo/provider/Counter.dart'; import 'package:provider_demo/widgets/menu.dart';
void main() { runApp( ChangeNotifierProvider( create: (context) => CounterModel(0), child: MyApp(), ), ); }
|
- ChangeNotifierProvider 将状态注入整个 widget 树。
- 所有子 widget 都可以通过 Provider.of、context.watch 或 context.read 获取数据。
计数器主页面实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(title: 'Provider'), ); } }
class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title;
@override _MyHomePageState createState() => _MyHomePageState(); }
class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), actions: [ IconButton( icon: Icon(Icons.golf_course), onPressed: () { Navigator.push(context, MaterialPageRoute(builder: (context) { return MenuView(); })); }, ), ], ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( "${Provider.of<CounterModel>(context).count}", style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); }
void _incrementCounter() { context.read<CounterModel>().add(); } }
|
一个简单的全局共享就完成了
注:不建议在程序入口初始化Provider,这里只是为了演示方便这么做,实际项目中要是都在程序入口初始化可能会导致内存急剧增加,除非是共享一些全局的状态,例如app日夜间模式切换,中英文切换等。
只要你在 runApp 或顶层 Widget 注入了 ChangeNotifierProvider,这个状态就存在 整个 widget 树中。 所有子页面都可以直接访问同一个状态实例,无需通过构造函数传值或返回值传递。那么怎么管理多个状态呢?
很简单,在main.dart中将:
1 2 3 4 5 6 7 8
| void main() { runApp( ChangeNotifierProvider( create: (context) => CounterModel(0), child: MyApp(), ), ); }
|
改为
1 2 3 4 5 6 7 8 9 10 11
| void main() { runApp( MultiProvider( providers: [ ChangeNotifierProvider(create: (context) => CounterModel(0)), ], child: MyApp(), ), ); }
|