Flutter入门 - 状态管理

266 阅读2分钟

概述

从命令式编程框架(iOS、Android)到声明式编程(Flutter、vue), 比如 Flutter 有大量的 State 需要来进行管理,数据共享的时候,

分类

  • 短时状态

某些简单的数据变化,只需要使用 statefulWidget 的 state 类自己管理即可

  • 应用状态AppState

比如用户信息,全局性的数据信息,需要选择全局状态管理的方式

共享状态管理

InheritedWidget

定义一个共享数据的 InheritedWidget,需要继承自 InheritedWidget

  • 需要定义一个 of 静态方法,通过传入 context 开始去查找祖先的数据
class ShareWidget extends InheritedWidget {
  //共享的 counter 数据
  final int counter;
  ShareWidget({this.counter, Widget child}) : super(child: child);
  
  static ShareWidget of(BuildContext context) {
    //沿着 context 往上查找最近的祖先 widget
    return context.dependOnInheritedWidgetOfExactType();
  }

  @override
  bool updateShouldNotify(covariant ShareWidget oldWidget) {
    //是否需要通知数据变更
    return this.counter != oldWidget.counter;
  }
}

其他 widget 使用

@override
Widget build(BuildContext context) {
  // 通过调用 of 静态方法拿到 “ShareWidget”
  int _counter = ShareWidget.of(context).counter;
  return Container(
    child: Text("$_counter"),
  );
}

Provider

官方推荐的全局状态管理工具

dependencies:
    provider:^4.0.4

三个概念

  • ChangeNotifier: 真正数据(状态)存放的地方
  • ChangeNotifierProvider: widget树中提供数据的地方,会在其中创建对应的 ChangeNotifier
  • Consumer: Widget 树中需要使用数据(状态)的地方

Provider 的基本用法

1.1 创建 provider

继承自 ChangeNotifier

class ViewModelProvider extends ChangeNotifier {
  // 1.创建自己想要共享的数据
  int _counter = 100;

  int get counter => _counter;

  set counter(int value) {
    _counter = value;
    notifyListeners();
  }
}

1.2 插入 provider

在应用顶层放入 ChangeNotifierProvider, 这样全局都可以使用 ViewModelProvider

void main() {
  runApp(ChangeNotifierProvider(
      create: (ctx) => ViewModelProvider(), child: HomePage()));
}

1.3 使用数据

1.3.1 Provider.of
int _counter = Provider.of<ViewModelProvider>(context).counter;
1.3.2 Consumer
  • 使用 Consumer 包裹要使用共享数据的 widget
  • builder 会返回三个参数
    • context 返回的是当前树的位置
    • provider ChangeNotifier对应的实例,可以直接使用 provider 中的数据
    • child 目的是防止 child 被重复 build
  • 当然使用 Consumer 的话,builder 所返回的 widget(例如demo中的FloatingActionButton) 仍然会被重复 build, 所以引入 Seletor
floatingActionButton: Consumer<ViewModelProvider>(
  child: Icon(Icons.add),
  //builder 会返回三个参数 context、provider、child
  builder: (cxt, provider, child) {
    return FloatingActionButton(
      child: child,
      onPressed: () {
        setState(() {
          provider.counter++;
        });
      },
    );
  },
),
1.3.3 Selector

关键参数

  • 1.泛型参数<A,S>
    • A: 我们这次要使用的 provider
    • S: 转换之后的数据类型
  • 2.seletor 回调函数
    • 转换的回调函数,没有转换的话,可以直接返回 Provider 的实例
  • 3.是否需要 rebuild
Selector({
  Key? key,
  required ValueWidgetBuilder<S> builder,
  required S Function(BuildContext, A) selector,
  ShouldRebuild<S>? shouldRebuild,
  Widget? child,
})
floatingActionButton: Selector<ViewModelProvider, ViewModelProvider>(
  selector: (cxt, provider) => provider,
  shouldRebuild: (pre, next) => false,
  builder: (cxt, provider, child) {
    print("Selector build");
    return FloatingActionButton(
      onPressed: () {
        provider.counter++;
      },
      child: child,
    );
  },
),
1.3.4 MultiProvider

开发中肯定不止一个 provider,不可能全部嵌套在 runApp中,因此使用 MultiProvider

可以定义一个 dart 类专门存放 providers

List<SingleChildWidget> providers = [
  ChangeNotifierProvider(
    create: (ctx) => Provider1(),
  ),
  ChangeNotifierProvider(
    create: (ctx) => Provider2(),
  ),
];
void main() {
  runApp(
    MultiProvider(
      // 使用
      providers: providers,
      child: HomePage(),
    ),
  );
}

使用小结

* 1.创建自己想要共享的数据  Provider extends ChangeNotifier
* 2.在应用顶层修改 runApp 添加 ChangeNotifierProvider
* 3.在其他位置使用共享数据
*   1)Provider.of<ViewModelProvider>(context)
*   2) Consumer (相对推荐)
*   3) Selector
* 4.使用 MultiProvider 管理多个共享状态