使用 Provider 改造屎山代码,代码量降低了 2/3!

前言
之前的几篇我们写了状态管理的机制和状态管理插件,接下来几篇我们就使用官方推荐的 Provider 来改造旧的代码,你会发现改造前后具有十分大的差别。
Provider 简介
Provider 是 Flutter 一个入门级的状态管理插件,基于 InheritedWidget 实现。Provider 能够沿着组件树共享状态数据,示例代码如下:
Provider类本身并不会在状态改变的时候自动更新子组件,因此更常用的是使用其子类:
ListenableProvider:监听实现了Listenable的的对象,并将其暴露给下级组件。当触发一个事件后会通知子组件依赖发生变化进而实现重建。ChangeNotifierProvider:最为常用的一个方式,是ListenableProvider的子类。监听实现了ChangeNotifier接口的对象,当该对象调用notifyListeners的时候,就会通知全部的监听组件更新组件。ValueListenableProvider:监听实现了ValueListenable接口的对象。当该对象改变时,会更新其下级组件。StreamProvider:监听 Stream 对象,然后将其内容暴露给子组件。通常是向一个组件以流的方式提供大量的内容,例如电池电量监测、Firebase 查询等。
如果一个对象被多个组件共享,那么可以使用如下方式:
在 Widget 中使用状态数据有三种方式:
使用
context.read<T>()方法:该方法返回 T 类型的状态数据对象,但不会监听该对象的改变,适用于只读的情况;使用
context.watch<T>()方法:该方法返回 T 类型状态数据对象,并且会监听它的变化,适用于需要根据状态更新的状况。使用
context.select<T,R>(R cb(T value))方法:返回 T 对象中的 R 类型对象,这可以使得Widget只监听状态对象的部分数据。
详细内容建议大家去看 Provider 的官方文档,我们后续的篇章也会涉及其中的内容。
代码分析
我们在前面的篇章介绍了一个动态模块的管理,包括了整个 CRUD 过程。具体可以从专栏阅读之前网络请求相关的篇章。首先我们来改造一下列表的代码,回头再来看之前的代码,就会知道为什么说直接使用 setState 的方式更新界面的开发者会被评为“草包”了!
之前代码一看就很乱,首先是在列表里包括了添加、编辑、删除的回调代码,是想要是业务复杂一点,岂不是回调要满屏飞了!其次是业务代码和 UI 代码混用,一个是代码又臭又长——俗称💩一样的代码,另外一个是业务代码的复用性降低了。比如说,我们在别的地方可能也会用到动态的增改删查业务,总不能再复制、粘贴再来一遍吧?
代码改造
现在我们来使用Provider 将业务和 UI 分离。将业务相关的代码统一放到状态管理中,UI 这边只处理界面相关的代码。首先抽取一个 DynamicModel 类,文件名为 dynamic_model.dart,把列表的相关业务代码放进来:
列表数据:使用一个
List<DynamicEntity>对象存储列表数据,默认为空数组。分页数据:当前页码
_currentPage,固定每页大小为 20。刷新方法:
refresh,将当前页码置为 1,重新请求第一页数据。加载方法:
load,将当前页码加 1,请求第 N 页的数据。获取分页数据:根据当前页面和分页大小请求动态数据,并更新列表数据。
预留
delete、add和update方法,以便后面的删除、添加和更新使用。
整个DynamicModel类的代码如下,这里关键的一点是使用 with ChangeNotifier 使得 DynamicModel 混入ChangeNotifer的特性,以便 ChangeNotifierProvider 能够为其添加监听器,并且在调用 notiferListeners的时候通知状态依赖的子组件进行更新。
接下来是使用 Provider 为动态模块提供状态管理,如前面的几章所述,Provider 需要处于组件的上级才能够为子组件提供状态共享,因此我们有两种方式来实现这种方式。
在构建
DynamicPage列表页面的app.dart中将DynamicPage作为Provider的下级。如下所示,这种方式的缺点是因为这是首页,如果各个模块的代码都往这里对方,会使得 app.dart 很臃肿,而且耦合度也变高。
使用一个
Widget包裹DynamicPage以及Provider来降低代码的耦合度,避免app.dart中的代码过于臃肿。
之后就是对 DynamicPage进行改造,首先是将 DynamicPage 由 StatefulWidget 改为 StatelessWidget,然后移除掉相关业务代码。,最后就是在 build 方法中从 Provider 获取界面所需的数据,或调用对应的方法。改造完的 DynamicPage 就十分清爽了,如下所示:
在 ListView.builder 中我们使用了 contxt.watch<DynamicModel>方法来获取最新的动态列表 ,从而使得当列表数据改变时能够刷新界面。而在调用方法方面,我们则使用了 context.read<DynamicModel>方法,因为这里并不需要监听状态的改变。运行一下,发现和之前的效果一样,改造完成。
改造前后对比
我们来对比改造前后的 DynamicPage 代码,如下图所示(左侧为旧代码)。可以看到,大部分代码都被移除了,实际原先的代码有120行,而现在的代码只有40行了,足足减少了 2/3!
当然,代码减少是因为将业务代码抽离了,但是业务代码本身是可以复用的。下一篇我们将删除、添加和编辑完成后,再来看 Provider 如何进一步提高代码复用性和简化页面代码。
总结
通过 Provider 状态管理,得到的最大的好处其实是 UI 层和业务层代码分离,精简了 UI 层代码的同时,也提高了业务代码的复用性。而 Provider 的局部刷新特性,也能够提高界面渲染的的性能。
版权声明: 本文为 InfoQ 作者【岛上码农】的原创文章。
原文链接:【http://xie.infoq.cn/article/64ec645fad65ce37957b77f12】。文章转载请联系作者。










评论