最近,在使用 Flutter 做一个图片分享的应用,自己创建出一套 Flutter 版的 MVVM 开发模式,觉得还挺好用,所以在此分享出来。
应用功能展示
首先,我们来看看我们这套MVVM开发模式,开发出来的应用是个什么样子,大概的一部分功能如下:(也可以点击观看 演示视频)
下拉刷新,如图:
上拉加载更多,如图:
点赞,如图:
缺省页(空数据),如图:
loading页,如图:
渐变的Appbar,如图:
评论,如图:
我的页面,如图:
以上只是 App 的一部分功能,大家也可以也可以点击观看 演示视频,或者扫描二维码下载 App(android) 体验:
Tip:App 可以使用登录账户信息: 任意一个作者名字,密码都是:666666,如:
用户名 | 密码 |
---|---|
persilee | 密码:666666 |
摄影师蝈蝈小姐 | 密码:666666 |
翠花小拍 | 密码:666666 |
在介绍这套 MVVM 开发模式之前,我们首先需要了解 riverpod
和 retrofit
是什么。
下面我们来分别了解他们是什么。
riverpod
riverpod 是 Flutter 状态管理库,flutter 的状态管理库有很多,例如: Redux
、 Bloc
、 Provider
等,flutter 官方推荐我们使用 provider
,一般我们使用 provider
的时候,会结合 ChangeNotifier
、 StateNotifier
、 freezed
去使用,而 riverpod
是 provider
的一个升级加强版,解决了 provider
一些疑难杂症,在这里就不过多介绍,如想了解更多 riverpod
信息,可以访问 riverpod官网 ,也可以参考我之前写的以下Demo 。
retrofit
retrofit 是一个网络请求库,做过 android 的同学应该比较熟悉,可以用注解的方式生成请求 Rest Api 的各种方法,如,以下的简单的用法:
1 | import 'package:retrofit/retrofit.dart'; |
更多详情可以访问 pub.dev retrofit 。
目录结构
接下来我们来看看项目的目录结构,如下:
1 | . |
从目录结构可知, models
、 pages
、view_model
分别是 MVVM 开发模式的 M(数据层)、 V(视图层)、 VM(通过riverpod的StateNotifier将数据层和视图层绑定,state变化时数据层也跟着变化,当然这里也可以处理一些页面逻辑),做过 android 的同学应该知道 android 的MVVM是使用 jetpack 组件库里的 DataBinding 和 LiveData 完成的,我这套开发模式灵感就是来源于此。
网络请求模块
首先,我们来对网络请求模块封装一把,让它能够通用易用。
retrofit 是依赖网络请求库的,我们可以选择不同的库,例如:http
、Dio
等。
在这里我们选择 Dio
,如下,是官方提供的案例代码:
1 | "https://5d42a6e2bc64f90014a56ca0.mockapi.io/api/v1/") (baseUrl: |
Dio的封装
它需要传一个 Dio 的实例和一个可选的 baseUrl,我们需要对这里重新封装一下,使用者不用传递任何参数就可以使用,也可以选择使用不同的网络库和 baseUrl;所以,我们要封装一个 baseDio
单例类,如果用户没有传,我们就传递一个默认的 baseDio
类,代码大概如下所示:
1 | 'https://api.lishaoy.net') (baseUrl: |
所以我要对 Dio
进行一次封装,代码如下:
1 | import 'package:dio/dio.dart'; |
BaseError的封装
以上代码中的 BaseError
类是一个抽象类,我们可以实现这个抽象类,告诉UI不同的错误类型,UI只需要用实现类就可以访问错误码和错误消息,代码如下:
1 | abstract class BaseError { |
网络模块的使用
这样我们的一个网络请求模块基本就封装好了,使用起来非常简单,首先我们需要定义接口,代码如下:
1 | 'https://api.lishaoy.net') (baseUrl: |
然后,我们会在 view model 使用它,如下:
1 | /** |
View Model 模块
View Model 模块主要处理数据和状态的绑定、业务逻辑等。
创建状态类
我们首先需要创建一个状态类,来存放数据状态和页面状态等,如下:
1 | /// 存储页面状态和数据状态(如,缺省页、错误页、加载中...) |
当然这个状态类也可以用 freezed
自动生成。
请求网络数据和处理页面状态
我们会返回这个状态类给UI,riverpod 的 StateNotifier 会监听这个状态类里的所有成员变量,当我们更改这些数据之后,UI会自动刷新,代码如下:
1 | /** |
以上一个方面就完成了应用首页的所有列表数据请求和页面状态处理,在UI层,不需要写 setState() 和 请求数据的任何代码,UI层只是呈现UI。
View 模块
那么在UI层怎么处理这些状态呢?
这也非常简单,代码如下:
1 | // 创建provider,返回viewModel |
是不是非常简单,不需要写 setState() 和 请求数据的任何代码,代码结构也非常清晰。在上述APP应用里的首页以及分类页面列表数据及页面的loading和缺省页等都是这一个简单 PostsPageCategory
完成的。
其他相关
以上这套开发模式我给出了大概的思路和部分代码,大家也可以顺着这个思路试试;这套开发模式后续还会继续优化它。
应用功能相关
用过 Flutter TabBar 同学应该知道,它在字体放大时会卡顿,以及如何自定义指示器等, 如图:
以及,渐变的高斯模糊背景和图片标题动画的实现等,如图:
及更多这个应用的功能实现和细节并没有在这里讲述,这篇文章主要介绍 MVVM,关于这个图片分享APP,只是我在业余时间对Flutter的研究探索和学习,这个应用大概只完成了一半,后续应该还好写关于这个APP的文章。
REST API接口相关
还有,这个APP的后端API也是我自己开发的,使用的是 nodejs 的 express + ts 开发的,如首页推荐接口及分类页接口数据都是通过这个API查询到的: 首页API接口
具体的实现是使用一条SQL语句查询得到,代码如下:
1 | SELECT |
这个是打印出来的log,具体的代码如下(可根据不同的参数查询不同的数据),如下:
1 | export const getPosts = async (options: GetPostOptions) => { |
如果这个后端 REST API 接口应用感兴趣的同学可以参考 宁皓网 的视频,我就是根据这套视频做的,不过自己加了很多东西。
最后,很多同学都希望我开源,所以,给出项目的地址,不过项目还没有完成,架构也在优化中…后续可能项目地址会变化,目前可以先参考以下地址: