您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
Flutter状态管理之InheritedWidget&Provider
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
Flutter状态管理之InheritedWidget&Provider
自猿其说Tech
2021-06-01
IP归属:未知
271680浏览
计算机编程
Flutter
# 一、Flutter 中基础的状态管理方式 ## 什么是状态管理 我们在开发业务的过程中有很多时间在处理 UI 和数据的交互,此时做的就是程序的状态管理,状态管理方案多种多样,选择一个合适的至关重要,它直接决定了你的程序结构。 可以通过一些 Flutter 框架内的组件和 Dart 语言的内置支持完成状态管理,也有很多不错的开源框架可以选择。 <center>![](//img1.jcloudcs.com/developer.jdcloud.com/ec32358b-694d-4e21-b0f1-7954cf84ecef20210601104457.png)</center> 左边一列是跟状态管理相关的一些原生组件及语言的内置支持。 右边一列是几个比较流行的第三方框架,它们的共同点是全部基于 InheritedWidget 实现状态共享。 ## StatefulWidget & State 在 Flutter 中每个 Widget 对象的状态都代表了一帧的状态,它是一个轻量级的对象,用来描述 UI 信息,是不可变的,虽然在语法上我们依然可以修改 Widget 对象属性的值,但是这样做会收到一个编译器警告,并且在下次刷新时由于 Widget 对象会重建,前面修改的数据将会丢失,而按照规范所有的 Widget 实例属性都必须使用 final 关键字来修饰,这样写在语义上就符合了不可变的原则。 但我们应用中的很多数据是在持续变化的,此时就需要将数据进行跨帧共享,Flutter 在一开始就为我们提供了一种状态管理方式,就是 StatefulWidget,当 StatefulWidget 被重建时,State 对象仍持可以保持,这样就实现了跨帧共享数据。 那么这个 State 对象是如何被跨帧保持的呢?由于 Widget 对象是被 Element 对象创建和持有,而 Element 对象在这个节点被移除前是一直存在的,也可以说它本身就是跨帧的,State 对象正是被保存在了 StatefulElement 对象中,每次刷新时 StatefulElement 对象会调用 State 对象的 build 方法,所以在 State 中就可以跨帧共享数据了。 StatefulWidget 是状态管理的基础,很多其它的状态管理方式都是基于它。我们在使用 StatefulWidget 管理状态时,通常会配合闭包回调或代理的方式实现刷新控制。当应用足够简单时,使用它是不错的选择,就像下面这样。 <center>![](//img1.jcloudcs.com/developer.jdcloud.com/e952372b-10b8-4de4-9aa4-864e71faba7e20210601104620.png)</center> 而一旦应用变得复杂起来,处理状态管理的成本会变的很高,这时候可能是下面这样的。 <center>![](//img1.jcloudcs.com/developer.jdcloud.com/933173e5-cd15-4cfa-9622-928becfd3b9320210601104808.png)</center> 当有上百个状态时,需要大量的闭包回调或代理,在一个承载了复杂业务的页面中,UI 层需要向 ViewModel 注册大量的闭包回调或代理,ViewModel 中需要区分什么时候调用哪个闭包回调或代理方法,而且在 Widget 树深度比较大的时候,数据共享、事件处理、细颗粒度的刷新控制都是比较麻烦的。 ## StreamBuilder、FutureBuilder StreamBuilder、FutureBuilder 也是 Flutter 内置的两个用来管理状态的组件,通过配合 Stream、Future,通过异步的方式进行刷新控制,它们内部也是通过 State.setState 来进行刷新。 StreamBuilder 比 FutureBuilder 的适用场景更多,需要细颗粒度的刷新控制时,有时 StreamBuilder 是个不错的选择。 ## ChangeNotifier、ValueNotifier <center>![](//img1.jcloudcs.com/developer.jdcloud.com/3b61c92b-e577-4daa-9ce1-1ed8fbd425ef20210601104930.png)</center> Listenable 和 ValueListenable 是抽象类,两个实现类又以 ChangeNotifier 使用居多,通过使 ViewModel 成为 ChangeNotifier 的子类型,在 State.initState 中监监听数据变化,ViewModel 通过调用 notifyListeners 方法通知 UI 进行刷新。 ## 第三方状态管理框架 Scoped Model 是早期官方推荐的一个框架,代码非常简单,但也只能实现一些简单场景的状态管理,现在已经被功能更强大的 Provider 替代,Flutter BloC 与 Provider 有很多相似之处,都是非常优秀的状态管理框架,Flutter Redux 轻度使用过,感觉不太适应,可能从 Web 端转过来的同学更喜欢吧,Fish Redux 同样也是功能强大的框架,使用这个框架后代码模版化会提升很多,但是它的代码入侵性很强,想更换的话成本很大,也是这里面学习曲线最陡的一个框架,由于 Provider 使用较多,比较熟悉,后面内容中介绍这个框架的部分会比较多。 # 二、InheritedWidget InheritedWidget 是 Flutter 中非常重要的一个功能型组件,它可以使数据在 Widget 树中自上而下传递,实现跨组件的数据共享。 Flutter 中一些内置组件正是使用 InheritedWidget 来共享状态, 例如:Theme、DefaultTextStyle、MediaQuery、DefaultTabController、ScrollConfiguration 等等。 <center>![](//img1.jcloudcs.com/developer.jdcloud.com/0b324271-0b93-4709-a7f7-630b4836730b20210601105132.png)</center> ## 使用 InheritedWidget 共享数据 <center>![](//img1.jcloudcs.com/developer.jdcloud.com/ae0b1af7-1da7-40df-bc6f-8e07347fe8f520210601105224.png)</center> ## InheritedWidget 的数据传递 <center>![](//img1.jcloudcs.com/developer.jdcloud.com/640886b8-0e17-40a2-b575-6e2efdb4b87a20210601110013.png)</center> 这里将关键源码摘了出来,并配上了注释,请配合上面的图来理解。 <center>![](//img1.jcloudcs.com/developer.jdcloud.com/03d221d3-6849-4ab4-a0b5-96b9954fc3a220210601110502.png)</center> <center>![](//img1.jcloudcs.com/developer.jdcloud.com/6b941cb9-8f0c-4b86-94c9-6152412b1e5f20210601110514.png)</center> ## InheritedWidget 刷新时通知依赖的流程 <center>![](//img1.jcloudcs.com/developer.jdcloud.com/c792f422-c1be-4128-a57f-2a1fe70598f720210601110609.png)</center> <center>![](//img1.jcloudcs.com/developer.jdcloud.com/8c81ea85-60d9-4527-b403-84a5ffbe633220210601111329.png)</center> <center>![](//img1.jcloudcs.com/developer.jdcloud.com/4bc27270-3ecd-4ee1-b91d-7a4ad443f44020210601111525.png)</center> <center>![](//img1.jcloudcs.com/developer.jdcloud.com/1bcb4b34-ac8e-4ff2-af0a-db7e3900c6cb20210601111539.png)</center> <center>![](//img1.jcloudcs.com/developer.jdcloud.com/cfad8250-7441-488e-bcda-68b3efbec0a720210601111549.png)</center> # 三、Provider 官方重点推荐,基于 InheritedWidget 实现跨 Widget 数据共享,通过监听ChangeNotifier、ValueNotifier、Stream、Future 来刷新 UI。 ## Provider 示例 <center>![](//img1.jcloudcs.com/developer.jdcloud.com/1dec633f-13ca-47f0-8750-848f612e9e7e20210601111648.png)</center> <center>![](//img1.jcloudcs.com/developer.jdcloud.com/115cd7a9-7517-4970-9d68-564de768d55c20210601111955.png)</center> <center>![](//img1.jcloudcs.com/developer.jdcloud.com/32f7f936-7e4e-4bdd-aa98-21dfff4756b020210601112036.png)</center> 更多 Provider 使用的相关资料,请查看官方文档,这里不做过多介绍。 ## 继承结构 <center>![](//img1.jcloudcs.com/developer.jdcloud.com/877fb4f2-4d1e-42e4-a7ac-4f5bd029f0f320210601112123.png)</center> ## Provider 节点 <center>![](//img1.jcloudcs.com/developer.jdcloud.com/e0461cf5-ae7d-4b73-a250-ea410c85de4f20210601112210.png)</center> ## 状态消费 <center>![](//img1.jcloudcs.com/developer.jdcloud.com/9414315e-9089-4a89-959d-d9a0ebf4fd0c20210601112249.png)</center> ## Provider 刷新的流程 Provider 不是用的 setState,是通过 _InheritedProviderScopeElement.markNeedsNotifyDependents() 方法里调用 markNeedsBuild(), 下一帧在 BuildOwner.buildScope() 里调用 rebuild -> performRebuild(), performRebuild() 里调用 Element 的 build(),ProxyElement 里的 build() 返回的是 widget.child,就是我们给 Provider 节点传的 widget, performRebuild() 会再调用 updateChild(),由于 build 里返回的还是上次的 widget, 所以不会再 rebuild 子树了, 但是在 _InheritedProviderScopeElement 里复写了 build(),在里面调用了 notifyClients(),去刷新依赖它的子孙节点。 ------------ ###### 自猿其说Tech-JDL京东物流技术发展部 ###### 作者:大件快运技术部-平台研发组 王志明 ------------
原创文章,需联系作者,授权转载
上一篇:Flutter异步编程中Completer的使用
下一篇:business rule engine研究
相关文章
Taro小程序跨端开发入门实战
Flutter For Web实践
配运基础数据缓存瘦身实践
自猿其说Tech
文章数
426
阅读量
2149963
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149963
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号