全局管理方案
全局状态管理是声明式 UI 框架必不可少的,flutter 官方推荐了两种全局管理的方式:
- inheritedWidget
- provider
inheritedWidget
inheritedWidget跟 react 的 context 功能类似,可以实现跨组件的数据共享。
使用步骤:
- 定义一个共享数据的
ContextWidget,继承自inheritedWidget - 将需要共享数据的组件包在 child 属性里
- 将数据定义在
ContextWidget的某个属性里 - 子组件通过
ContextWidget.of(context).xxx来接受数据
具体案例如下:
dart1import 'package:flutter/material.dart'; 2import 'pages/main/main.dart'; 3 4void main(List<String> args) { 5 return runApp(MyApp()); 6} 7 8class MyApp extends StatelessWidget { 9 const MyApp({Key? key}) : super(key: key); 10 11 12 Widget build(BuildContext context) { 13 return MaterialApp( 14 home: HYHomePage(), 15 ); 16 } 17} 18 19class HYHomePage extends StatefulWidget { 20 const HYHomePage({Key? key}) : super(key: key); 21 22 23 State<HYHomePage> createState() => _HYHomePageState(); 24} 25 26class _HYHomePageState extends State<HYHomePage> { 27 // ContextWidget里的 state 不能改变,所以在这里定义一个 state,传给 ContextWidget 28 int count = 100; 29 30 Widget build(BuildContext context) { 31 return Scaffold( 32 appBar: AppBar(title: Text('inherited')), 33 // 这里将所有需要共享数据的组件包起来 34 // 由于数据需要更改,所以在更高级的`State`中将数据传给 ContextWidget 35 body: ContextWidget( 36 count: count, 37 child: Column( 38 children: [ 39 ShowData01(), 40 ShowData02(), 41 ], 42 )), 43 floatingActionButton: FloatingActionButton( 44 child: Icon(Icons.edit), 45 onPressed: () { 46 setState(() { 47 count += 1; 48 }); 49 }), 50 ); 51 } 52} 53 54class ContextWidget extends InheritedWidget { 55 // 定义状态,接受到上层给的数据 56 final int count; 57 // 定义构造方法 58 ContextWidget({required this.count, required Widget child}) 59 : super(child: child); 60 // 定义方法 61 static ContextWidget? of(BuildContext context) { 62 // 沿着 Element 树找到最近的 ContextElement 取出 widget 对象 63 return context.dependOnInheritedWidgetOfExactType(); 64 } 65 66// 如果返回 true,会执行依赖当前InheritedWidget的 state 中的didChangeDependencies 67 68 bool updateShouldNotify(ContextWidget oldWidget) { 69 return oldWidget.count != count; 70 } 71} 72 73class ShowData01 extends StatefulWidget { 74 const ShowData01({Key? key}) : super(key: key); 75 76 77 State<ShowData01> createState() => _ShowData01State(); 78} 79 80class _ShowData01State extends State<ShowData01> { 81 82 void didChangeDependencies() { 83 super.didChangeDependencies(); 84 print('updateShouldNotify返回 true 则会调用我'); 85 } 86 87 88 Widget build(BuildContext context) { 89 // 这里接受数据 90 int? count = ContextWidget.of(context)?.count; 91 return Container( 92 color: Colors.red, 93 child: Text('当前计数是:$count'), 94 ); 95 } 96} 97 98class ShowData02 extends StatelessWidget { 99 const ShowData02({Key? key}) : super(key: key); 100 101 102 Widget build(BuildContext context) { 103 // 这里接受数据 104 int? count = ContextWidget.of(context)?.count; 105 return Card( 106 color: Colors.blue, 107 child: Text('当前计数是:$count'), 108 ); 109 } 110}
Provider
官方目前推荐的全局状态管理工具,由官方维护。
安装依赖:
bash1 $ flutter pub add provider
使用:
dart1import 'package:provider/provider.dart';
Provider 三个概念:
-
ChangeNotifier
真正数据(状态)存放的地方
-
ChangeNotifierProvider
Widget 树中提供数据(状态)的地方,会在其中创建对应的 ChangeNotifier
-
Consumer
Widget 树中需要使用数据(状态)的地方
使用步骤:
-
创建
ChangeNotifier这里创建两个
ChangeNotifiercount_model.dart
dart1import 'package:flutter/material.dart'; 2 3// 1.创建需要共享的数据 4class CountVModel extends ChangeNotifier { 5 int _counter = 100; 6 int get counter => _counter; 7 set counter(int val) { 8 _counter = val; 9 notifyListeners(); 10 } 11}user_model.dart
dart1import 'package:flutter/material.dart'; 2 3// 1.创建需要共享的数据 4class UserVModel extends ChangeNotifier { 5 String _userName = 'qiuyanxi'; 6 int _age = 20; 7 String get userName => _userName; 8 String getUserInfo() { 9 return "姓名:$userName 年龄$_age"; 10 } 11}
-
使用ChangeNotifierProvider
main.dart
dart1List<SingleChildWidget> providers = [ 2 ChangeNotifierProvider(create: (context) => CountVModel()), 3 ChangeNotifierProvider(create: (context) => UserVModel()) 4];这里为了便于多个状态管理,所以将
ChangeNotifierProvider都放进数组里,传给MultiProviderdart1void main(List<String> args) { 2 //2.顶层使用MultiProvider包裹,实现多个ChangeNotifierProvider的结合 3 return runApp(MultiProvider( 4 providers: providers, 5 child: MyApp(), 6 )); 7}用
MultiProvider包裹,一般选择在全局包裹,这里在最顶层runApp里使用。 -
用 select、Consumer、Provider.of 读取数据
dart1class MyApp extends StatelessWidget { 2 const MyApp({Key? key}) : super(key: key); 3 4 5 Widget build(BuildContext context) { 6 return MaterialApp( 7 home: HYHomePage(), 8 ); 9 } 10} 11 12class HYHomePage extends StatelessWidget { 13 const HYHomePage({Key? key}) : super(key: key); 14 15 16 Widget build(BuildContext context) { 17 int count = Provider.of<CountVModel>(context).counter; 18 19 return Scaffold( 20 appBar: AppBar(title: Text('全局管理')), 21 body: Center( 22 child: Column( 23 mainAxisAlignment: MainAxisAlignment.center, 24 children: [ 25 ShowData01(), 26 ShowData02(), 27 ShowData03(), 28 ], 29 ), 30 ), 31 // 5.如果不依赖数据,则使用 Selector修改数据, 32 floatingActionButton: Selector<CountVModel, CountVModel>( 33 builder: (context, vModel, child) => FloatingActionButton( 34 child: child, 35 onPressed: () { 36 vModel.counter += 1; 37 }, 38 ), 39 selector: (context, vModel) => vModel, //这里可以转换数据 40 shouldRebuild: (pre, now) => false,// 是否需要重新构建,返回 false 不再需要重新构建 41 child: Icon(Icons.add), // 不需要重新 builder 的icon,可以放child 里传进去 42 ), 43 ); 44 } 45} 46 47class ShowData01 extends StatelessWidget { 48 const ShowData01({Key? key}) : super(key: key); 49 50 51 Widget build(BuildContext context) { 52 // 3. 使用Provider.of读取数据 53 int count = Provider.of<CountVModel>(context).counter; 54 return Container( 55 color: Colors.red, 56 child: Text( 57 '当前计数是:${count}', 58 style: TextStyle(fontSize: 20), 59 ), 60 ); 61 } 62} 63 64class ShowData02 extends StatelessWidget { 65 const ShowData02({Key? key}) : super(key: key); 66 // 4.使用 Consumer读取,也可以修改数据 67 68 Widget build(BuildContext context) { 69 return Card( 70 color: Colors.blue, 71 child: Consumer<CountVModel>( 72 builder: (context, vmodel, child) => Text( 73 '当前计数是:${vmodel.counter}}', 74 style: TextStyle(fontSize: 20), 75 ))); 76 } 77} 78 79class ShowData03 extends StatelessWidget { 80 const ShowData03({Key? key}) : super(key: key); 81 // 4.使用 Consumer2读取全部数据,也可以修改数据 82 83 Widget build(BuildContext context) { 84 return Card( 85 color: Colors.yellow, 86 child: Consumer2<CountVModel, UserVModel>( 87 builder: (context, countVmodel, userVmodel, child) => Column( 88 children: [ 89 Text( 90 '当前计数是:${countVmodel.counter}}', 91 style: TextStyle(fontSize: 20), 92 ), 93 Text( 94 '用户信息是:${userVmodel.getUserInfo()}}', 95 style: TextStyle(fontSize: 20), 96 ), 97 ], 98 ))); 99 } 100}