Flutter——sdk:状态管理provider
Posted wzj_what_why_how
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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方法,将数据暴露出来
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()
));
Consumer
- 问题: 使用 Provider.of 获取资源,可以得到资源暴露的数据的读写接口,在实现数据的共享和同步上还是比较简单的。
但是,滥用 Provider.of 方法也有副作用,那就是当数据更新时,页面中其他的子 Widget 也会跟着一起刷新。
那么,有没有办法能够在数据资源发生变化时,只刷新对资源存在依赖关系的 Widget,而其他 Widget 保持不变呢? - 解决:基于 Consumer, Provider 可以精确地控制 UI 刷新粒度。Consumer 使用了 Builder 模式创建 UI,收到更新通知就会通过 builder 重新构建 Widget。
body: Center(
child: Consumer<CountModel>(
builder: (context,CountModel counter,_) => Text('第二个页面count:$counter.counter'),
),
),
floatingActionButton: Consumer<CountModel>(
builder: (context, CountModel counter, child) => FloatingActionButton(
onPressed: counter.increment,
child: child,
),
child: MyIcon(),
)
Selector
Selector和Consumer是等价的,也是通过Provider.of获取数据的,不同的是,Selector正如他的名字一样,
他会过滤掉一些不必要的数据更新从而阻止重新构建,也就是说Selector只会更新符合条件的数据。
-
Selector<A, S>中的泛型:
- 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<T?>>();对值进行监听。
而_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组件位于父节点位置,这样子节点才能共享数据状态,
- 其次我们尽可能的减少我们刷新的颗粒度,最好在使用数据的地方进行刷新组件。
以上是关于Flutter——sdk:状态管理provider的主要内容,如果未能解决你的问题,请参考以下文章