数据是我自己从 APP 上扒下来的,仅做测试效果
ps:顺便要吐槽的是,我扒数据的时候接口返回的还是满28元免赔送,最近我使用叮咚买菜的时候,它竟然涨到了39元免赔送(资本家真是黑心)
接下来,讲讲这个简单的项目采用的技术.
项目SDK 基于">=2.12.0 <3.0.0",全面支持空安全版本,以 Get 为基础框架,做路由管理,状态管理,实现远程路由,本地路由同步,逻辑与界面分离.
下面是你能看到的三方
UI方面
- 下拉刷新
pull_to_refresh: ^2.0.0 - toast
fluttertoast: ^8.0.7 - easyloading
flutter_easyloading: ^3.0.3 - loading
loading_animations: ^2.2.0 - 骨架图
skeleton_loader: ^2.0.0+4 - 轮播
flutter_swiper_null_safety: ^1.0.2
数据方面
- json序列化
json_serializable: ^6.1.3 - 屏幕适配
flutter_screenutil: ^5.0.0+2 - 缓存
shared_preferences: ^2.0.5 - 设备信息
device_info: ^2.0.0
框架层面
- GetX,项目管理框架
get: ^4.1.4 - 通过 scheme 打开APP 获取 参数
uni_links: ^0.5.1
三方这个东西,我从 iOS 开发开始,一直秉持的一个想法就是,前期三方一定要找高星,会持续维护的框架,如果有需要,自己再 fork 一下做局部修改,最好是做持续更新,其他的一些测试性三方我暂时就不发出来了,有条件的可以选择性的自己去重写一些 UI 层的轮子,比如轮播,loading 什么的,练练技术,不过目前我处于 flutter 探索阶段,这一步暂缓.
业务方面
其实也没啥好讲的,业务其实大家都一样,稍微讲讲我做的数据分离吧
以 Category分类页面为例.
index 为 import 入口文件.
view 层逻辑代码为
class CategoryPage extends GetView<CategoryController> {
const CategoryPage({Key? key}) : super(key: key);
// 内容页
Widget _buildView() {
return Column(
children: [
CategoryHeaderView(),
Container(
height: 0.1,
color: Colors.blue,
child: FittedBox(
fit: BoxFit.none,
alignment: Alignment.topCenter,
child: Container(
// padding: EdgeInsets.only(top: 10),
width: ScreenUtil().screenWidth,
height: 20,
color: AppColors.lightMain),
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
),
clipBehavior: Clip.antiAlias,
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Lefter(),
CategoryDetailListView(),
],
),
),
),
],
);
}
@override
Widget build(BuildContext context) {
return GetBuilder<CategoryController>(
init: CategoryController(),
builder: (_) {
return Scaffold(
body: controller.state.isLoad ? _buildView() : skeleton(),
);
},
);
}
}
可以看到 在 view 层,我使用GetView<CategoryController>去创建页面
abstract class GetView<T> extends StatelessWidget {
const GetView({Key? key}) : super(key: key);
final String? tag = null;
T get controller => GetInstance().find<T>(tag: tag)!;
@override
Widget build(BuildContext context);
}
GetView 继承自StatelessWidget,提供了一个 controller 泛型,因为数据完全由 controller 控制刷新,所以我们不需要使用 stful.
controller持有 state,controller 做逻辑处理,state 持有数据
controller
class CategoryController extends GetxController {
CategoryController();
final state = CategoryState();
/// UI 组件
final RefreshController refreshController = RefreshController(
initialRefresh: true,
);
final ScrollController scrollController = ScrollController();
final ScrollController headScrollController = ScrollController();
// tap
void handleTap(int index) {
Get.snackbar(
"标题",
"消息",
);
}
***其他业务,数据加载,滚动监听等等***
}
State
state 的数据其实放 controller 也行,但是如果想项目结构更加清晰,可以讲 controller 的数据与逻辑再进行一次分离,分离出一个 state,反正页面刷新都是由 controller 控制的
class CategoryState {
Category? model;
CategoryDetail? detail;
bool isLoad = false;
/// 是否展示顶部右侧按钮
bool isHeadAllShow = false;
// 头部当前选中
final headCurrentIndex = 0.obs;
/// 左侧当前选中
final lefterCurrentIndex = 0.obs;
/// 透视图
List<Cate> get headModels => model?.cate ?? [];
/// 左侧数据
List<Cate> get lefterModels =>
headModels.itemNullable(headCurrentIndex.value)?.cate ?? [];
/// 左侧数据
List<Product> get righterModels => detail?.products ?? [];
}
由于 category 点击事件都是联动逻辑(头部点击联动左侧数据+右侧详情),所以我大部分使用的是controller.update(),obx 监听在这个页面中没有提现太多使用.(最近拜读了某个大牛的 get 源码解析,对 obx 的认识更深了一步,有机会贴出来一起学习)
这个 demo 其实我也稍微做了一些比较细的小点,比如分类页面头部分类选择时,保证选中的 item 居于最中间,也做了一些动画效果,又比如分类下拉菜单页,做的一个overlayEntry转场进入的选择框效果. 还有首页部分,为了底部的四宫格效果,写了一堆的 array 拖展等等后续应该不会再对这个叮咚 UI进行 编写了,毕竟是扒的数据,影响不好
我一开始的目的是冲着编写一个flutter快速项目的 脚手架进行的,叮咚的 UI 只是测试 flutter 的 widgetUI 编写,目前这个脚手架还不丰富,三方登录,分享,地图,音视频播放,图片选择,dio 请求,加密,im 业务,数据库缓存,浏览器跳转打开 APP 等等.
所以,后续,我会走两条路子
一是将我所在的公司业务项目利用 flutter,写出"大部分"内容(真实网络请求)
二是继续丰富我的脚手架,顺便做写 demo 测试.
更新不易,如有帮助到各位看官的,点个喜欢, 谢谢啦~
网友评论