前言
上一篇我简要介绍了flutter和app的概况,有兴趣的朋友可以先看一下上一篇:我的Flutter初体验-1CSDN/酷安。
之前有朋友提出我的项目接近mvi架构,不过我认为更接近于mvc和mvi的结合:
- app的页面由三部分组成:view、state和logic(getx常用结构),其中view负责页面展示,state负责存储页面状态,logic负责数据获取、数据处理、状态刷新和页面刷新;
- 与mvc架构相比,view对应v,而state和logic对应m和c;
- 与mvi架构相比,view对应v,state和logic对应m和i,虽然状态均保存在state中,但是没有专门的intent层;
- 所以我的项目既不属于mvc架构,也不属于mvi架构(业余选手的辩词)。
在上一篇中,我将app大体分为了ui端和逻辑端两个部分,下面我将介绍ui端的要点。
ui端
在ui端,我认为主要有状态管理和异步显示两个要点,下面一一进行介绍。
状态管理
ui界面的每个组件都有自己的状态,这些状态包括数据、颜色等,状态管理主要指的是动态组件的状态更新问题。
- stateful widget
在flutter中,页面上每个组件都被称作widget,根据组件的状态或数据是否会发生改变,分为stateless widget
和stateful widget
两种。其中stateless widget的状态和数据不会发生改变,stateful widget的状态和数据可能会发生改变。
若app的某个组件的状态会发生改变,就可以把这个组件定义为stateful widget,同时定义其对应的state
,该state专门负责维护此widget的状态。在需要更新组件状态时,调用state的setState方法即可刷新组件状态。
采用这种状态管理方法,一个widget的状态可以由自身管理,也可以交给父widget管理。
- getx
除了官方提供的状态管理方法外,也有很多第三方库也提供了状态管理方法,其中,被称为flutter开发神器的getx
就提供了GetBuilder
和响应式
两种状态管理方法。
- GetBuilder
- GetBuilder是一种简单状态管理方法,在使用时仍需要手动调用方法实现组件刷新;
- 在使用时,可以将动态组件定义为stateless widget,使用GetBuilder包裹,并指定其对应的controller;
- 当组件的状态改变时,可以直接对状态变量进行赋值,再在controller中调用update()方法更新组件;
- 通过分析源码,可以看出,GetBuilder实际上在原有组件外面包裹了一层stateful widget,是对官方状态管理方法的简化。通过GetBuilder,我们可以在同一处管理所有的状态并进行组件刷新,简化了代码复杂度。
- 响应式
- 响应式状态管理指的是变量与组件绑定,只要变量发生数据变化,那么组件就会自动刷新,而无需手动调用刷新方法;
- 在使用时,需要将状态变量定义为响应式变量(getx提供的.obs扩展),并在组件中使用该响应式状态变量的value;
- 当响应式状态变量的value被重新赋值后,getx就会主动调用组件刷新的方法;
- 通过分析源码,我认为getx的响应式状态管理实际上采用了观察者模式,在响应式状态变量被赋新值时,就会通知订阅者执行更新操作。
GetBuilder和响应式状态管理都属于全局状态管理方法,即:所有的widget的状态不论层级,均进行统一管理。
我的项目中采用了getx的GetBuilder方法进行状态管理。除了状态管理之外,getx也提供了依赖注入、路由管理等功能的解决方案,大大简化了项目结构,提升了开发效率(不过flutter官方好像不是很喜欢getx)。
异步显示
在我的项目中,扫描、查询等都是耗时操作,为了不影响用户界面,这些耗时操作都需要在后台执行,并异步显示结果。下面将介绍几种我了解到的异步显示方法。
- FutureBuilder/StreamBuilder
前文提到,Future和Steam是dart的两种异步编程方式,Future可以返回一个异步操作的结果,而Stream可以返回一系列异步时间的结果,FutureBuilder和StreamBuilder正是基于这两种异步操作构建的异步显示组件。
- builder是flutter中用来创建widget的函数,FutureBuilder和StreamBuilder的用法和普通widget相同;
- 在FutureBuilder中,必须定义一个返回值为Future的异步函数,根据该函数的状态返回不同的widget;
- 在StreamBuilder中,必须定义一个返回值为Steam的异步函数,根据Stream中的事件返回不同的widget;
可以看出,FutureBuilder只能根据异步操作是否执行完毕,返回两种不同的widget;而StreamBuilder则可通过监听一系列事件,返回多种不同的widget,可以实现粒度更细的异步显示。
在ui界面的编写中,FutureBuilder和StreamBuilder完全可以替代普通的widget,在我的项目中,扫描结果的显示正是通过FutureBuilder实现的,若扫描操作尚在执行中,就返回loading界面;若扫描操作执行结束,就返回扫描结果列表,如下图所示:
-
扫描函数执行中,展示loading界面:
-
扫描函数执行完毕,展示扫描结果:
上述功能同样可以采用StreamBuilder实现,甚至效果更好!这也是我的一个改进方向。
- Future/Stream+手动刷新
不用官方的提供的组件,我们自己也可以用Future/Stream配合手动刷新实现异步显示。
- 对于Future来说,只需要在执行结果返回后对状态变量赋值,并刷新组件即可;
- 对于Stream来说,可以订阅该Stream,并在每次监听到事件时更新数据并刷新组件以实现异步显示。
相对于FutureBuilder和StreamBuilder,我认为Future/Stream+手动刷新的方法更自由,也能够实现更为复杂的功能,比如:可以在不同位置订阅同一个Stream,以便根据同一个异步操作进行多个位置的组件刷新。
剩下的
本来想在这一篇中把ui端和逻辑端都介绍完,但是不知不觉ui端就写了这么多了。因为我不想写太长的文章(懒),所以逻辑端将会在下一篇中介绍。
上面的介绍可能存在错误或者疏漏的地方,请大家批评指正~
最后附上app和项目地址:
- app地址:蓝奏云 密码:a5cv
- 项目地址:Device Detector