Flutter 工程化框架选择 — 状态管理何去何从
本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!
这是 《Flutter 工程化框架选择》 系列的第六篇 ,就像之前说的,这个系列只是单纯告诉你,创建一个 Flutter 工程,或者说搭建一个 Flutter 工程脚手架,应该如何快速选择适合自己的功能模块,或者说这是一个指引系列,所以比较适合新手同学。
其实这是我最不想写的一个篇。
状态管理是 Flutter 里 ?? 的话题,本质上 Flutter 里的状态管理就是传递状态和基于setState的封装,状态管理框架解决的是如何更优雅地共享状态和调用setState。
那为什么我不是很想写状态管理的对比内容?
首先因为它很繁,繁体的煩,从 Flutter 发布到现在,scoped_model、BLoC、Provider、flutter_redux、MobX、fish_redux、Riverpod、GetX等各类框架“百花齐放”,虽然这对于社区来说是这是好事,但是对于普通开发者来说很容易造成过度选择困难症,特别早期不少人被各种框架“伤害过”。
其次,状体管理在 Flutter 里一直是一个“敏感”话题,每次聊到状态管理就绕不开GetX,但是一旦聊GetX又会变成“立场”问题,所以一直以来我都不是很喜欢写状态管理的内容。
所以本来应该在第一篇就出现的内容,一直被拖到现在才放出来,这里提前声明一些,本篇不会像之前一样从大小和性能等方面去做对比,因为对于状态管理框架来说这没什么意义:
集成后对大小的影响可能还不如一张图片性能主要取决于开发者的习惯,在状态管理框架上对比性能其实很主观当然,如果你对集成后对大小的影响真的很在意,那可以在打包时通过--analyze-size来生成 analysis.json 文件用于对比分析:
flutterbuildapk--target-platformandroid-arm64--analyze-size
上诉命令在执行之后,会在/Users/你的用户名/.flutter-devtools/目录下生成一个apk-code-size-analysis_01.json文件,之后我们只需要打开 Flutter 的 DevTools 下的App Size Tooling就可以进行分析。
例如这里是将Riverpod和GetX在同一项项目集成后导出不同 json 在 Diff 进行对比,可以看到此时差异也就在 78.5kb ,这个差异大小还不如一张 png 资源图片的影响大。
所以本次主要是从这些状体管理框架自身的特点出发,简单列举它们的优劣,至于最后你觉得哪个适合你,那就见仁见智了~
本篇只是告诉你它们的特点和如何去选择,并不会深入详细讲解,如果对实现感兴趣的可以看以前分享过的文章:
Flutter Riverpod 全面深入解析
全面理解 State 与 Provider
全面深入理解状态管理设计
Provider2019 年的 Google I/O 大会 Provider 成了 Flutter 官方新推荐的状态管理方式之一,它的特点就是:不复杂,好理解,代码量不大的情况下,可以方便组合和控制刷新颗粒度, 其实一开始官方也有一个 flutter-provide ,不过后来宣告GG , Provider 成了它的替代品。
??注意,provider比flutter-provide多了个r,所以不要再看着 provide 说 Provider 被弃坑了。
简单来说,Provider 就是针对InheritedWidget的一个包装工具,他让InheritedWidget的使用变得更简单,在往下共享状态的同时,可以通过ChangeNotifier、Stream、Future配合Consumer*组合出多样的更新模式。
所以使用 Provider 的好处之一就是简单,同时你可以通过Consumer*等来决定刷新的颗粒度,其实也就是BuildContext在of(context)时的颗粒度控制。
登记到InheritedWidget里的 context 决定了更新是 rebuild 哪个ComponentElement,感兴趣的可以看全面理解 State 与 Provider
当然,虽然一直说 Provider 简单,但是其实还是有一些稍微“复杂”的地方,例如select。
Provider 里select是对BuildContext做了 “二次登记” 的行为,就是以前你用 context 是watch的时候 ,是直接把这个 Widget 登记到 Element 里,有更新就通知。
但是select做了二次处理,就是用dependOnInheritedElement做了颗粒化的判断,如果是不等于了才更新,所以它对 context 有要求,如下图对就是对 context 类型进行了判断。
所以select算是 Provider 里的“小魔法“之一,总的来说Provider 是一个符合 Flutter 的行为习惯,但是不大符合前端和原生的开发习惯的优秀状态管理框架。
优点:
简单好维护read、watch、select 提供更简洁的颗粒度管理官方推荐缺点:
相对依赖 Flutter 和 Widget需要依赖 Context最后顺带辟个谣,之前有 “传闻” Provider 要被弃坑的说法,作者针对这个也有相应对澄清,所以你还是可以继续安心使用 Provider。
RiverpodRiverpod和 Provider 是同个作者,因为 Provider 存在某些局限性,所以作者根据 Provider 这个单词重新排列组合成 Riverpod。
如果说 Provider 是InheritedWidget的封装,那 Riverpod 就是在 Provider 的基础上重构出更灵活的操作能力,最直观的就是Riverpod 中的 Provider 可以随意写成全局,并且不依赖BuildContext来编写我们需要的业务逻。
注意:Riverpod 中的 Provider 和前面的 Provider 没有关系。
在 Riverpod 里基本是每一个 “Provider” 都会有一个自己的 “Element” ,然后通过WidgetRef去 Hook 后成为BuildContext的替代,所以这就是 Riverpod 不依赖 Context 的 “魔法” 之一
??这里的 “Element” 不是 Flutter 概念里三棵树的Element,它是 Riverpod 里Ref对象的子类。Ref主要提供 Riverpod 内的 “Provider” 之间交互的接口,并且提供一些抽象的生命周期方法,所以它是 Riverpod 里的独有的 “Element” 单位。
另外对比 Provider ,Riverpod 不需要依赖 Flutter ,所以也不需要依赖Widget,也就是不依赖BuildContext,所以可以支持全局变量定义 “Provider” 对象。
优点:
在 Provider 的基础上更加灵活的实现,不依赖BuildContext,所以业务逻辑也无需注入BuildContextRiverpod 会尽可能通过编译时安全来解决存在运行时异常问题支持全局定义ProviderReference能更好解决嵌套代码缺点:
实现更加复杂学习成本提高目前从我个人角度看,我觉得 Riverpod 时当前之下状态管理的最佳选择,它灵活且专注,体验上也更符合 Flutter 的开发习惯。
注意,很多人一开始只依赖riverpod然后发现一些封装对象不存在,因为riverpod是不依赖 flutter 的实现,所以在 flutter 里使用时不要忘记要依赖flutter_riverpod。
BLoCBLoC算是 Flutter 早期比较知名的状态管理框架,它同样是存在bloc和flutter_bloc这样的依赖关系,它是基于事件驱动来实现的状态管理。
flutter_bloc基于事件驱动的核心就是Stream和 Provider, 是的,flutter_bloc依赖于 Provider,然后在其基础上设计了基于Stream的事件响应机制。
所以严格意义上 BLoC 其实是 Provider +Stream,如果你一直很习惯基于事件流开发模式,那么 BLoC 就很适合你,但是其实从我个人体验上看,BLoC 在开发节奏上并不是快,相反还有点麻烦,不过优势也很明显,基于Stream的封装可以更方便做一些事件状态的监听和转换。
BlocSelectorBlocA,BlocAState,SelectedState(
selector:(state){
//returnselectedstatebasedontheprovidedstate.
},
builder:(context,state){
//returnwidgetherebasedontheselectedstate.
},
)
MultiBlocListener(
listeners:[
BlocListenerBlocA,BlocAState(
listener:(context,state){},
),
BlocListenerBlocB,BlocBState(
listener:(context,state){},
),
BlocListenerBlocC,BlocCState(
listener:(context,state){},
),
],
child:ChildA(),
)
优点:
代码更加解耦,这是事件驱动的特性把状态更新和事件绑定,可以灵活得实现状态拦截,重试甚至撤回缺点:
需要写更多的代码,开发节奏会有点影响接收代码的新维护人员,缺乏有效文档时容易陷入对着事件和业务蒙圈项目后期事件容易混乱交织类似的库还有rx_bloc,同样是基于Stream和 Provider , 不过它采用了 rxdart 的Stream封装。
flutter_reduxflutter_redux 虽然也是 pub 上的 Flutter Favorite 的项目,但是现在的 Flutter 开发者应该都不怎么使用它,而恰好我在刚使用 Flutter 时使用的状态管理框架就是它。
其实前端开始者对 redux 可能会更熟悉一些,当时我恰好用 RN 项目切换到 Flutter 项目,在 RN 时代我就一直在使用 redux,flutter_redux 自然就成了我首选的状态管理框架。
其实这也是 Flutter 最有意思的,很多前端的状态管理框架都可以迁移到 Flutter ,例如 flutter_redux 里就是利用了Stream特性,通过redux单向事件流的设计模式来完成解耦和拓展。
在 flutter_redux 中,开发者的每个操作都只是一个Action,而这个行为所触发的逻辑完全由middleware和reducer决定,这样的设计在一定程度上将业务与UI隔离,同时也统一了状态的管理。
当然缺陷也很明显,你要写一堆代码,开发逻辑一定程度上也不大符合 Flutter 的开发习惯。
优点:
解耦对 redux 开发友好适合中大型项目里协作开发缺点:
影响开发速度,要写一堆模版不是很贴合 Flutter 开发思路说到 redux 就不得不说fish_redux,如果说 redux 是搭积木,那闲鱼最早开源的 fish_redux 可以说是积木界的乐高,闲鱼在redux的基础上提出了Comoponent的概念,这个概念下fish_redux是从Context、Widget等地方就开始全面“入侵”你的代码,从而带来“超级赛亚人”版的redux。
所以不管是 flutter_redux 还是 fish_redux 都是很适合团队协作的开发框架,但是它的开发体验和开发过程,注定不是很友好。
GetXGetX可以说是 Flutter 界内大名鼎鼎,Flutter 不能没有 GetX 就像程序员不能没有 PHP ,GetX 很好用,很具备话题,很全面同时也很 GetX。
严格意义上说现在 GetX 已经不是一个简单的状态管理框架,它是一个统一的 Flutter 开发脚手架,在 GetX 内你可以找到:
状态管理路由管理多语言支持页面托管Http GetConnectRx GetStream各式各样的 extension可以说大部分你想到的 GetX 里都有,甚至还有基于 GetX 的 get_storage 实现纯 Dart 文件级 key-value 存储支持。
所以很多时候使用 GetX 开发甚至不需要关心 Flutter ,当然这也导致经常遇到的奇怪情况:大家的问题集中在 GetX 里如何 xxxx,而不是 Flutter 如何 xxxx,所以 GetX 更像是依附在 Flutter 上的解决方案。
当然,使用 GetX 最直观的就是不需要BuildContext,甚至是你在路由跳转时都不需要关心 Context ,这就让你的代码看起来很“干净”,把整个开发过程做到“面向 GetX 开发”的效果。
另外 GetX 和 Provider 等相比还具备的特色是:
Get.put、Get.find、Get.to等操作完全无需 Widget 介入内置的extension如各类基础类似的*.obs通过GetStream实现了如var count = 0.obs;和Obx(() = Text("${controller.name}"));这样的简化绑定操作那 GetX 是如何脱离 Context 的依赖?说起来也不复杂,例如 :
GetMaterialApp内通过一个会有一个GlobalKey用于配置MaterialApp的navigatorKey,这样就可以通过全局的navigatorKey获取到Navigator的State,从而调用pushAPI 打开路由
Get.put和Get.find是通过一个内部全局的静态Map来管理,所以在传递和存放时就脱离了InheritedWidget,结合Obx,在对获取到的GetxController的 value 时会有个addListener的操作,从而实现Stream的绑定和更新
可以说 GetX 内部有很多“魔法”,这些魔法或者是对 Flutter API 的 Hook、或者是直接脱离 Flutter 设计的自定义实现,总的来说 GetX “有自己的想法”。
这也就带来一个了个问题,很多人新手一上手就是 GetX ,然后对 Flutter 一知半解,特别是深度解绑了 Context 之后,很多 Flutter 问题就变成了 GetX 上如何 xxxx,例如前面的:Flutter GetX 如何调用谷歌地图这种问题。
如果使用 GetX 而不去思考和理解 GetX 的实现,就很容易在 Flutter 的路上走歪,比如上面各种很基础的问题。
这其实也是 GetX 的最大问题:GetX 做的很多,它入侵到很多领域,而且它拥有很多“魔法”,这些“魔法”让 Flutter 开发者不知布局的脱离了本来应有的轨迹。
当然,你说我就是想完成需求,好用就行,何必关心它们的实现呢?从这个角度看 GetX 无疑是非常不错的选择,只要 GetX 能继续维护下去并把“魔法”继续兼容。
大概就是:GetX “王国” 对初级开发者友好,但是“魔法全家桶”其实对社区的健康发展很致命。
优点:
瑞士军刀式护航对新人友好可以减少很多代码缺点:
全家桶,做的太多对于一些使用者来说是致命缺点,需要解决的 Bug 也多“魔法”使用较多,脱离 Flutter 原本轨迹入侵性极强总的来说,GetX 很优秀,他帮你都写好了很多东西,省去了开发者还要考虑如何去组合和思考的过程,从我个人的角度我不喜欢这种风格,但是它总归是可以帮助你提高开发效率。
另外还有一个状态管理库Mobx,它库采用了和 GetX 类似的风格,虽然 Mobx 的知名度和关注度不像 GetX 那么高,但是它同样采用了隐式依赖的模式,某种意义上可以把Mobx看成是只有状态管理版本的 GetX。
最后通过上面分享的内容,相信大家对于选哪个状态管理框架应该有自己的理解了,还是那句废话,采用什么方案和框架具体还是取决于你的需求场景,不管是哪个框架目前都有坑和局限,重点还是在于它未来是否持续维护,或者不维护了你自己能否继续维护下去。
最后,如果你还有什么疑问,或者针对 Flutter 工程选择上还有哪些茫然,欢迎留言评论。
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线