全局管理方案

全局状态管理是声明式 UI 框架必不可少的,flutter 官方推荐了两种全局管理的方式:

  1. inheritedWidget
  2. provider

inheritedWidget

inheritedWidget跟 react 的 context 功能类似,可以实现跨组件的数据共享。

使用步骤:

  1. 定义一个共享数据的 ContextWidget,继承自inheritedWidget
  2. 将需要共享数据的组件包在 child 属性里
  3. 将数据定义在ContextWidget的某个属性里
  4. 子组件通过ContextWidget.of(context).xxx来接受数据

具体案例如下:

dart
1import '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

官方目前推荐的全局状态管理工具,由官方维护。

安装依赖:

bash
1 $ flutter pub add provider

使用:

dart
1import 'package:provider/provider.dart';

Provider 三个概念:

  • ChangeNotifier

    真正数据(状态)存放的地方

  • ChangeNotifierProvider

    Widget 树中提供数据(状态)的地方,会在其中创建对应的 ChangeNotifier

  • Consumer

    Widget 树中需要使用数据(状态)的地方

使用步骤:

  1. 创建ChangeNotifier

    这里创建两个ChangeNotifier

    count_model.dart

    dart
    1import '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

    dart
    1import '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}
  1. 使用ChangeNotifierProvider

    main.dart

    dart
    1List<SingleChildWidget> providers = [
    2  ChangeNotifierProvider(create: (context) => CountVModel()),
    3  ChangeNotifierProvider(create: (context) => UserVModel())
    4];

    这里为了便于多个状态管理,所以将ChangeNotifierProvider都放进数组里,传给MultiProvider

    dart
    1void main(List<String> args) {
    2  //2.顶层使用MultiProvider包裹,实现多个ChangeNotifierProvider的结合
    3  return runApp(MultiProvider(
    4    providers: providers,
    5    child: MyApp(),
    6  ));
    7}

    用MultiProvider包裹,一般选择在全局包裹,这里在最顶层 runApp 里使用。

  2. 用 select、Consumer、Provider.of 读取数据

    dart
    1class 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}