• Flutter——sdk:状态管理【provider】


    provider

    provider初识

    • 状态管理框架
    • Provider 用于提供数据, 它的目标就是完全替代 StatefulWidget。

    不同类型的provider

    • Provider 最基础的 provider 组成,接收一个任意值并暴露它。
    • ListenableProvider 供可监听对象使用的特殊 provider。ListenableProvider 会监听对象,并在监听器被调用时更新依赖此对象的 widgets。
    • ChangeNotifierProvider 为 ChangeNotifier 提供的 ListenableProvider 规范,会在需要时自动调用 ChangeNotifier.dispose。【目前项目实践中用到这个比较多】
    • ValueListenableProvider 监听 ValueListenable,并且只暴露出 ValueListenable.value。
    • StreamProvider 监听流,并暴露出当前的最新值。
    • FutureProvider 接收一个 Future,并在其进入 complete 状态时更新依赖它的组件。

    三个类

    三个类分成了三个步骤:

    • ChangeNotifier:创建数据 Model。用于提供notifyListeners()方法,达到自动通知更新 UI。
    • ChangeNotifierProvider:创建顶层共享数据,持有 with ChangeNotifier 的model。
    • Consumer: 控制Widget的刷新颗粒度。

    ChangeNotifier:

    • 用于向监听器发送通知。换言之,如果被定义为 ChangeNotifier,你可以订阅它的状态变化。(和观察者模式相类似)。

    网上的一个例子说明:

    ///这里需要混入ChangeNotifier
    class CounterModel with ChangeNotifier {
      int _count;///唯一字段_count(下划线代表私有)
      CounterModel(this._count);
    
      ///写一个增加的方法。然后需要调用notifyListeners();这个方法是通知用到Counter对象的widget刷新用的。
      void add() {
        _count++;
        notifyListeners();
      }
      get count => _count;//get方法,将数据暴露出来
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    tip:唯一一行和 ChangeNotifier 相关的代码就是调用 notifyListeners()。当模型发生改变并且需要更新 UI 的时候可以调用该方法。而剩下的代码就是 CartModel 和它本身的业务逻辑。
    ChangeNotifier 是 flutter:foundation 的一部分,而且不依赖 Flutter 中任何高级别类。


    ChangeNotifierProvider

    • ChangeNotifierProvider widget 可以向其子孙节点暴露一个 ChangeNotifier。
    • ChangeNotifierProvider.value 不仅能够提供数据供子孙节点使用,还可以在数据改变的时候通知所有听众刷新。(通过之前我们说过的 notifyListeners)
    • ChangeNotifierProvider 放在什么位置:在需要访问它的 widget 之上。
    void main() {
      runApp(ChangeNotifierProvider.value(
          value: CounterModel(),// 需要共享的数据资源
          child: MyApp()
      ));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Consumer

    • 问题: 使用 Provider.of 获取资源,可以得到资源暴露的数据的读写接口,在实现数据的共享和同步上还是比较简单的。
      但是,滥用 Provider.of 方法也有副作用,那就是当数据更新时,页面中其他的子 Widget 也会跟着一起刷新。
      那么,有没有办法能够在数据资源发生变化时,只刷新对资源存在依赖关系的 Widget,而其他 Widget 保持不变呢?
    • 解决:基于 Consumer, Provider 可以精确地控制 UI 刷新粒度。Consumer 使用了 Builder 模式创建 UI,收到更新通知就会通过 builder 重新构建 Widget。
    body: Center(
      child: Consumer(
        builder: (context,CountModel counter,_) => Text('第二个页面count:${counter.counter}'),
      ),
    
    ),
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    floatingActionButton: Consumer(
        builder: (context, CountModel counter, child) => FloatingActionButton(
            onPressed: counter.increment,
            child: child,
        ),
        child: MyIcon(),
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Selector

    Selector和Consumer是等价的,也是通过Provider.of获取数据的,不同的是,Selector正如他的名字一样,
    他会过滤掉一些不必要的数据更新从而阻止重新构建,也就是说Selector只会更新符合条件的数据。

    • Selector中的泛型

      • A是我们从顶层获取的Provider的类型
      • S是我们关心的具体类型,也就是获取到的Provider中真正对我们有用的类型,需要在selector 中返回该类型。这个Selector的刷新范围也从整个Provider变成了 S。
    • Selector的中的属性:

      • selector:就是一个Function,入参会将我们获取的顶层 provider传入,然后再返回我们所关心的S。
      • shouldRebuild:这个属性会储存selector过滤后的值,也就是selector返回的S 并拿收到通知之后新的S与缓存的S进行比较,
        以此来判断这个Selector是否需要重新构建,默认preview!=next就刷新,如果是collection,selector进行深度比较。
      • builder:和Consumer一样,这里返回的是要构建的控件,第二个参数provider,就是我们刚才selector中返回的S。
      • child:这个用于优化一些不用刷新的部分,和Consumer一样。


    实现原理

    • Provider是Flutter官方开发维护的,也是近些年官方最为推荐的状态管理库。其特点是:不复杂、好理解,可控度高。
    • Provider主要是对 InheritedWidget 组件进行上层封装,使其更易用,通过ChangeNotifier来处理数据,从而减少了InheritedWidget的大量模版代码。
    • 从源码上我们可以看到Provider直接继承于InheritedProvider,通过工厂构造函数Provider.value传入model和child节点,
      然后通过context.dependOnInheritedWidgetOfExactType<_InheritedProviderScope>();对值进行监听。
      而_InheritedProviderScope就是继承于InheritedWidget的。

    优缺点

    • 【优点】使用简单。model继承ChangeNotifier,没有更多的布局widget,只需要通过context.read / context.watch操作或者监听model即可;
    • 【优点】颗粒度把控简单。为了解决widget重新build太频繁的问题,官方推出了context.select来监听对象的部分属性。也可使用Consumer/Selector进行布局;
    • 【优点】基于官方InheritedWidget的封装,不存在任何风险,很稳定且不会给性能方面加负担
    • 【缺点】context强关联,有Flutter开发经验的都知道,context大多时候基本都是在widget中才能获取到,
      在其他地方想随时获取 BuildContext 是不切实际的,也就意味着大多时候只能在view层去获取到Provider提供的信息。

    注意事项

    • 注意provider组件位于父节点位置,这样子节点才能共享数据状态,
    • 其次我们尽可能的减少我们刷新的颗粒度,最好在使用数据的地方进行刷新组件。
  • 相关阅读:
    JavaScript 数组字符串方法
    springboot项目的打包发布部署,jar和war的区别
    【luogu AGC034F】RNG and XOR(FWT)
    第十六章 源代码文件 REST API 教程(一)
    【泛函分析】紧性
    MySQL压缩包方式安装,傻瓜式教学
    指针练习(2)
    1个月时间整理了2019年上千道Java面试题,近500页文档!
    uni-app之使用App.vue全局文件的教学
    双目视觉实战--相机几何
  • 原文地址:https://blog.csdn.net/wzj_what_why_how/article/details/126546664