Flutter 状态管理
InheritedWidget跨组件的数据传递
InheritedWidget和React中的context功能类似,可以实现跨组件数据的传递。
定义一个共享数据的InheritedWidget,需要继承自InheritedWidget
1、InheritedWidget创建
2、方法调用
这里通过HYCounterWidget.of(context)去获取最近节点的HYCounterWidget,然后获取里面的counter。
int counter = HYCounterWidget.of(context).counter;
注意:InheritedWidget仅仅用来共享数据,不做数据同步。
在页面间做数据的状态同步或更新,我们常用的是官方推荐的第三方库:provider
provider实现全局数据的同步
provider对于同步数据有两种方式:
- Provider.of
- consumer
区别:
通过Provider.of获取变量之后,在变量发生更新时,整个widget会重新进行build。Consumer在刷新整个Widget树时,会尽可能少的rebuild Widget
Provider.of使用基本步骤:
在使用Provider的时候,我们主要关心三个概念:
1、ChangeNotifier:真正数据(状态)存放的地方
2、ChangeNotifierProvider:Widget树中提供数据(状态)的地方,会在其中创建对应 ChangeNotifier
3、Consumer:Widget树中需要使用数据(状态)的地方
第一步:创建自己的ChangeNotifier
- 这里我们可以使用继承自ChangeNotifier,也可以使用混入,这取决于概率是否需要继承自其它的类
- 我们使用一个私有的_counter,并且提供了getter和setter
- 在setter中我们监听到_counter的改变,就调用notifyListeners方法,通知所有的Consumer进行更新
第二步:在Widget Tree中插入ChangeNotifierProvider
我们需要在Widget Tree中插入ChangeNotifierProvider,以便Consumer可以获取到数据:
将ChangeNotifierProvider放到了顶层,这样方便在整个应用的任何地方可以使用CounterProvider
(注意:在ChangeNotifierProvider中的HYCounterViewModel是懒加载,只有第一个consumer用到时才会过来加载。)
第三步:在首页中使用Consumer引入和修改状态
引入位置一:在floatingActionButton中使用Consumer,当点击按钮时,修改CounterNotifier中的counter数据;
引入位置二:在body中使用Consumer,Consumer需要传入一个builder回调函数,当数据发生变化时,就会通知依赖数据的Consumer重新调用builder方法来构建;
Consumer的builder方法解析:
参数一:context,每个build方法都会有上下文,目的是知道当前树的位置
参数二:ChangeNotifier对应的实例,也是我们在builder函数中主要使用的对象
参数三:child,目的是进行优化,如果builder下面有一颗庞大的子树,当模型发生改变的时候,我们并不希望重新build这颗子树,那么就可以将这颗子树放到Consumer的child中,在这里直接引入即可(注意我案例中的Icon所放的位置)
Selector优化consumer多余渲染问题
- 比如当点击了floatingActionButton时,我们在代码的两处分别打印它们的builder是否会重新调用;
- 我们会发现只要点击了floatingActionButton,两个位置都会被重新builder;
- 但是floatingActionButton的位置有重新build的必要吗?没有,因为它是否在操作数据,并没有展示;
- 如何可以做到让它不要重新build了?使用Selector来代替Consumer
Selector和Consumer对比,不同之处主要是三个关键点:
- 关键点1:泛型参数是两个
- 泛型参数一:我们这次要使用的Provider(原始数据)
- 泛型参数二:转换之后的数据类型,比如我这里转换之后依然是使用CounterProvider,那么他们两个就是一样的类型(转换之后的数据)
- 关键点2:selector回调函数
- 转换的回调函数,你希望如何进行转换
- S Function(BuildContext, A) selector
- 我这里没有进行转换,所以直接将A实例返回即可
- 关键点3:是否希望重新rebuild
- 这里也是一个回调函数,我们可以拿到转换前后的两个实例;
- bool Function(T previous, T next);
- 因为这里我不希望它重新rebuild,无论数据如何变化,所以这里我直接return false;
MultiProvider解决接收多个状态数据
同一widget需要同时接收多个状态数据,需要采用multiprovider进行处理;
1、将多个provider放到一个List列表中,
2、通过multiprovider进行包裹
3、在触发值改变的selector中设置多个值
4、在值变化的consumer中接收多个值并进行数据处理
ChangeNotifierProxyProvider 同步更新数据
当一个对象的更新会影响到另外一个数据更新的时候,采用ChangeNotifierProxyProvider 进行数据关联更新。
这里HYFilterViewModel(过滤条件)的更新会对HYMealViewModel(内容)进行更新。