1 为什么需要状态管理?

(1) 状态管理的作用

image.png
在编写一个应用的过程中,我们有大量的State需要来进行管理,而正是对这些State的改变,来更新界面的刷新:
image.png

(2) 状态管理 分类

1) 短时状态

某些状态只需要在自己的Widget中使用即可
比如:

  • 我们之前做的简单计数器counter
  • 一个PageView组件记录当前的页面
  • 一个动画记录当前的进度
  • 一个BottomNavigationBar中当前被选中的tab

这种状态我们只需要使用StatefulWidget对应的State类自己管理即可,Widget树中的其它部分并不需要访问这个状态。

2) 全局状态

开发中也有非常多的状态需要在多个部分进行共享
比如:

  • 用户一个个性化选项
  • 用户的登录状态信息
  • 一个电商应用的购物车
  • 一个新闻应用的已读消息或者未读消息

这种状态我们如果在Widget之间传递来、传递去,那么是无穷尽的,并且代码的耦合度会变得非常高,牵一发而动全身,无论是代码编写质量、后期维护、可扩展性都非常差。
这个时候我们可以选择全局状态管理的方式,来对状态进行统一的管理和应用。

2 全局状态管理

(1) InheritedWidget (不推荐)

InheritedWidget,可以实现跨组件数据的传递。

  • of方法通过context开始去查找祖先的Widget
  • updateShouldNotify方法是对比新旧Widget,是否需要对更新相关依赖的Widget ```dart import ‘package:flutter/material.dart’;

void main() { runApp(const MyApp()); }

class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key);

// This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: ‘Flutter Demo’, theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(title: ‘Flutter Demo Home Page’), ); } }

class CounterWidget extends InheritedWidget { final int counter; CounterWidget({required this.counter, required Widget child}) : super(child: child);

static CounterWidget of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType(); }

@override bool updateShouldNotify(InheritedWidget oldWidget) { // 如果返回为true, 执行state中的didChangeDependencies return counter != oldWidget.counter; } }

class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, required this.title}) : super(key: key); final String title;

@override State createState() => _MyHomePageState(); }

class _MyHomePageState extends State { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: CounterWidget( counter: 100, child: Column(children: [ ShowData1(), ShowData2(), ])), ); } }

class ShowData1 extends StatelessWidget { const ShowData1({Key? key}) : super(key: key);

@override Widget build(BuildContext context) { int counter = CounterWidget.of(context).counter; return Card( color: Colors.blue, child: Text(“当前计数: $counter”), ); } }

class ShowData2 extends StatelessWidget { const ShowData2({Key? key}) : super(key: key);

@override Widget build(BuildContext context) { int counter = CounterWidget.of(context).counter; return Container( child: Text(“当前计数: $counter”), ); } }

  1. <a name="sYO48"></a>
  2. ## (2) Provider (推荐)
  3. Provider底层依赖于InheritedWidget, 使得共享状态更方便<br />在pubspec中添加依赖:
  4. > provider: ^6.0.2
  5. ```dart
  6. import 'package:flutter/material.dart';
  7. // 1 创建需要共享的数据
  8. class CounterProvider extends ChangeNotifier {
  9. int _counter = 100;
  10. int get counter {
  11. return _counter;
  12. }
  13. set counter(int value) {
  14. _counter = value;
  15. notifyListeners();
  16. }
  17. }
  1. import 'package:fltest/viewmodel/viewmodel.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:provider/provider.dart';
  4. void main() {
  5. runApp(
  6. // 2 在Widget Tree中插入ChangeNotifierProvider
  7. ChangeNotifierProvider(
  8. create: (ctx) => CounterProvider(),
  9. child: const MyApp(),
  10. ));
  11. }
  12. class MyApp extends StatelessWidget {
  13. const MyApp({Key? key}) : super(key: key);
  14. // This widget is the root of your application.
  15. @override
  16. Widget build(BuildContext context) {
  17. return MaterialApp(
  18. title: 'Flutter Demo',
  19. theme: ThemeData(
  20. primarySwatch: Colors.blue,
  21. ),
  22. home: const MyHomePage(title: 'Flutter Demo Home Page'),
  23. );
  24. }
  25. }
  26. class MyHomePage extends StatefulWidget {
  27. const MyHomePage({Key? key, required this.title}) : super(key: key);
  28. final String title;
  29. @override
  30. State<MyHomePage> createState() => _MyHomePageState();
  31. }
  32. class _MyHomePageState extends State<MyHomePage> {
  33. @override
  34. Widget build(BuildContext context) {
  35. return Scaffold(
  36. appBar: AppBar(
  37. title: Text(widget.title),
  38. ),
  39. body: Container(
  40. alignment: Alignment.center,
  41. child: Column(
  42. mainAxisAlignment: MainAxisAlignment.center,
  43. children: [
  44. ShowData1(),
  45. ShowData2(),
  46. ],
  47. ),
  48. ),
  49. // 4 消费-写
  50. floatingActionButton: Consumer<CounterProvider>(
  51. builder: (context, value, child) => FloatingActionButton(
  52. child: child,
  53. onPressed: () {
  54. value.counter += 1;
  55. },
  56. ),
  57. child: Icon(Icons.add), // 性能优化, 避免不必要的重新构建
  58. ),
  59. );
  60. }
  61. }
  62. class ShowData1 extends StatelessWidget {
  63. const ShowData1({Key? key}) : super(key: key);
  64. @override
  65. Widget build(BuildContext context) {
  66. // 3 监听
  67. int counter = Provider.of<CounterProvider>(context).counter;
  68. return Container(
  69. color: Colors.blue,
  70. child: Text("当前计数: $counter"),
  71. );
  72. }
  73. }
  74. class ShowData2 extends StatelessWidget {
  75. const ShowData2({Key? key}) : super(key: key);
  76. @override
  77. Widget build(BuildContext context) {
  78. return Container(
  79. color: Colors.red,
  80. // 5 消费-读
  81. child: Consumer<CounterProvider>(
  82. builder: (context, value, child) {
  83. return Text("当前计数: ${value.counter}");
  84. },
  85. ),
  86. );
  87. }
  88. }

1) 使用Selector替换Consumer进行性能优化

作用:

  • 对原有的数据进行格式转换 (前 -> 后)
  • 选择是否重新构建builder返回的widget
    1. floatingActionButton: Selector<CounterProvider, CounterProvider>(
    2. selector: (ctx, value) => value,
    3. shouldRebuild: (prev, next) => false,
    4. builder: (context, value, child) => FloatingActionButton(
    5. child: child,
    6. onPressed: () {
    7. value.counter += 1;
    8. },
    9. ),
    10. child: Icon(Icons.add), // 性能优化, 避免不必要的重新构建
    11. ),

    (3) MultiProvider

    1. runApp(MultiProvider(
    2. providers: [
    3. ChangeNotifierProvider(create: (ctx) => CounterProvider()),
    4. ChangeNotifierProvider(create: (ctx) => UserProvider()),
    5. ],
    6. child: MyApp(),
    7. ));