原文地址:
https://juejin.cn/post/6967645147207041054
前言
前些日子朋友咨询可否爬网站数据,并制作一Windows程序。作为开发iOS/Android/MacOS/WatchOS/...的我没接触过C++/C#,用Java开发也不现实,重新认识Windows组件、网络、资源管理还是比较耗时的,于是打算推荐其使用众包平台发布任务。转念一想,大Flutter岂不是最合适之选,「Write once, Run anywhere」,可以避免平台兼容难、学习成本高等众多问题,最终在笔者挖坑填坑之旅中更加确定,Flutter会是移动端的未来。
本文为笔者利用下班和周末时间边学边做项目的历程,希望对于Flutter入门者有所帮助。其中大多为开发建议、推荐和思路,并非代码级的指引。阅读本文大约需20分钟。
**
一、准备工作
1、Flutter开发环境、开发工具、学习资料
① 工欲善其事,必先利其器。
开发Flutter如果想要同时打包iOS&Android,必须要有一台Mac电脑,没有怎么办?发挥你程序员的优势,安装MacOS虚拟机并在苹果官网下载Xcode安装包。
建议使用Android Studio(AS)开发,下载Flutter和Dart插件即可,更好兼容安卓模拟器。
笔者电脑安装有Xcode和AS,所以只需安装Flutter SDK即可,安装后使用万能指令,flutter doctor,发现问题,解决问题。
注:Android license status unknown的警告是因为我本地java版本过高,不影响开发,可以忽略。
当然,也可以使用Visual Studio开发,开发Windows程序必选。并且编译打包EXE文件时,必须在Windows10电脑环境运行。
(所以笔者在项目收尾阶段安装Windows10虚拟机并安装VS后才能打包,占用我电脑40G的存储。🤣🤣🤣)
②推荐一些学习Flutter的学习资料网站:
- Flutter中文网:https://flutterchina.club
字节跳动-幸福里FE团队某大神制作,入门最佳选择,保姆级资料。
- Flutter官网:https://flutter.dev,官网中文:https://flutter.cn
官网资料,最为可靠。版本新,资源全,手把手视频教学及在线练习体验。
- 未曾相识的老孟大哥:http://laomengit.com
最全的中文控件库,使用与需求更贴切的良心官方控件,省时省心又省力。
吐槽一下用Android写UI,Android(ListView,RecycleView)输于iOS(UITableView,UICollectionView)的原因,就是iOS基本不需要自定义适配器。但Android使用XML写UI,非常方便,强于Xib。
- 第三方控件库:https://pub.flutter-io.cn
相当于iOS之Cocoapods,Android之jcenter。常用第三方库可在中文网资料中获取,这里不一一列举。
强调一下,第三方库的叠加依赖会导致兼容失败,一般情况下,使用any版本即可,pub管理工具会自动下载不造成冲突的版本。特殊情况需要下载离线库进行配置。某些工具库在编译时需要注释掉,例如hive_generator,自动生成model属性转换方法时需要添加,编译时不注释会报错Dart Error: error: import of dart:mirrors is not supported in the current Dart runtime
- 阿里Flutter-GO:https://github.com/alibaba/flutter-go
值得推荐,以成品介绍组件。可惜的是可能是因为《闲鱼》App性能问题,阿里内部对Flutter热情不再,2年前已暂停维护。关于性能问题,目前的Flutter2.2已经有不小的性能提升和生态支持完善。
含高仿抖音,斗鱼,豆瓣,开源中国。基本包含市场App所有功能板块。
2、了解分析需求,爬取需求数据,确定开发流程
①需求
- 表格展示专业列表,可操作、可分页。
- 多维度多条件查询
- 方案列表,编辑、切换方案
- 按需导出Excel表格
- 注册,登录,Token
②爬取网站数据
凡是爬取网站里面不让爬取数据的行为都是不道德/违法的。
实际上,各公司之间有很多数据都是相互爬。但这也没有抵消笔者愧疚之心。况且此网站要爬取的接口都是有经过MD5加密后的动态Token的,所以再次打算推荐其使用众包平台。
在JS调试窗口,除标头还有预览页面。惊喜的是,网站的数据按500条/页展示在预览中,也就是说,我们只需要拷贝50页数据即可。😂😂😂
用命令行touch data{0..49}.json
创建50个文件,依次粘贴文本。制作一个小工具,融合50个json文件得到1个20M的json文件,这样我们就得到原始数据。
当然,后续对数据查找和性能的优化过程中,按照本科、专科、体育、艺术、提前类型分为5个json文件,查找速度从原来的600ms提高到400ms以内。
你问我为什么不直接粘贴在一个json文件里?有兴趣的朋友可以试用任何编辑器打开20M的json文件。
~~
③开发流程
- 创建新Flutter App,默认已勾选iOS、Android,选择MacOS及Windows平台,若不能勾选,
flutter config --enable-macos-desktop
及flutter config --enable-windows-desktop
,重新创建。所有的开发都可以在Mac上进行,最后打包的时候再无缝移植到Windows。 - UI,数据库,交互,逻辑,工具等。
- 平台兼容,原生开发,Flutter与原生通信交互。
- 完善应用信息,规正权限,提交审核。
- 应用已上架Mac App Store,应用地址为《选校方案》,Windows版稍后发布在云盘中。
二、功能模块
遍历《选校方案》的功能模块,分析其中使用的Flutter知识,从iOS和Android开发者视角来解释。
1、UI:万物基于Widget。通俗来讲,StatefulWidget是可变的控件,StatelessWidget是不可变的控件。单从创建UI来讲,Android dev更有优势一些,因为XML也是嵌套模式。iOS dev接触过SwiftUI或者Rx系列或者RAC更容易理解,嵌套型UI是把iOS中所有约束,属性,控件,参数,交互、动画融合在一起,不以控件为主要对象,形成的响应式UI。从开发便利性来说,笔者感觉Hot Reload和响应式编程是所有移动端的趋势。
①避免嵌套,以封装治理地狱式套娃
- 不论是单人还是多人开发,如果无休止的添加单个自定义Widget,一个复杂页面动辄几千行。对于后期维护者来说,结构调整困难,逻辑交互入口混杂难以分辨,简直难受至极。
- 常用UI模块进行封装,减少代码量,使结构清晰。
- 在这里推荐Bloc模式<可理解为MVP>,简单页面还是以StatefulWidget为主,如有多个页面共享同一数据的业务场景,就非常适合。
- 整个UI布局封装后在Bulid内也只有25行代码。
@override
Widget build(BuildContext context) {
passValue = ModalRoute.of(context).settings.arguments;
return Scaffold(
body: Column(
children: [
_getTopSearchView(context), //顶部搜索栏
Container(
height: 40,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int position) {
return _getTitleColumn(position, Global.titles[position]);
},
itemCount: Global.titles.length,
controller: firstRowController,
),
),//数据表头
Expanded(child: Container(child: _getCoreListView())),//数据核心区
_getPageView()//分页区
],
),
);
}
- 以部分UI举例,将宽度、边框属性、居中数据、背景颜色进行封装。代码篇幅较长,以图片替代展示。
②整体布局的思路
- 标题搜索栏以外层Wrap嵌套,流式布局可以使控件居中并设置行/列间距,自动适配整体窗口宽度。Dropdown、TextFiled、Button在iOS、Android中也有类似控件,唯一不同的是,获取控件的动态值时,并不是直接以控件的属性值来获取,而是通过TextEditingController监听、动态字符串变量等方式。
Wrap(
alignment: WrapAlignment.center,
spacing: 20,
runSpacing: 10,
children: []
)
- 数据表头设置为横向ListView,添加ScrollController,与数据展示区的ScrollController相互监听,完成同步滚动功能。
//监听第一行变动
firstRowController.addListener(() {
double offset = secondedRowController.offset;
double offset1 = firstRowController.offset;
if (offset1 != offset) {
secondedRowController.jumpTo(offset1);
}
});
//监听第二行变动
secondedRowController.addListener(() {
double offset = secondedRowController.offset;
double offset1 = firstRowController.offset;
if (offset != offset1) {
firstRowController.jumpTo(offset);
}
});
- 数据展示区的UI相当复杂,既要保证横竖向都能滚动,又要考虑滚动性能,还要展示{500条/页}数据懒加载。
笔者耗时整整两天才搞定此功能,一开始使用官方控件DataTable,发现在自定义单元格展示不同UI时,需大量代码匹配其代理方法;滚动性能极差;不可懒加载,遂代码全部废弃,重新布局。
受益于iOS构建UI时的灵活性处理,笔者突发奇想,使用横向SingleChildScrollView内部嵌套竖向ListView,ListView使用itemBuilder构建。这样一来,横竖向滚动,性能,懒加载全部拥有。
单条item最后的“添加/删除”按钮操作,刷新UI几乎无卡顿。
- 分页区:若为固定长度,会导致Flutter UI的经典(Overflow)屏幕溢出问题,黄黑斜条纹显示区域为UI警告(驾考宝典中为立面标记,一般在隧道口🤣),所以外层以SingleChildScrollView嵌套,窗口较小时,不会出现此警告。
更换100条/页或其他页码时,使用实例变量来标记单页条数,数组的getRange方法重新分割数据,刷新ListView与页码数。
2、数据库
Hive,pub上1.8KLike,GitHub上2.3kStar,在Flutter中算是欢迎度比较高的。
- 配合hive_generator使用,可存储模型,使用方便。
- 不必写SQL语句,可直接按数组/Map形式存储
- CURD速度极快
项目中建表方式:
- 创建方案列表库,与专业数据库相互关联。在每个方案中包含专业列表。
- 方案model中增加isCurrentPlan属性,切换现有方案时正反赋值。
- 创建用户属性库,存储用户手机号、密码、昵称、角色权限(普通角色只能看到1000条数据)、已登录状态
shared_preferences,类似于iOS之NSUserDefaults,Android之SharedPreferences,可用于本地小数据存储。
3、第三方库、全局文件及资源库
- 麻雀虽小五脏俱全,项目小但功能全,添加第三方库时,使用flutter pub get/upgrade等命令,本App集成的第三方库有:
- 常用功能及第三方库列表
- pubspec.yaml是Flutter项目的核心配置文件,iOS dev需要能接受非可视化配置,这点Android dev习以为常,与build.gradle相似。包含本地图片,本地文件,第三方库,字体等。
- 创建Global类文件,存放常用常量/变量,便于程序调用。
4、与原生通信,原生适配。
- 用Xcode/AS单独打开各自平台文件夹中的项目,应用名称、图标、启动图等需原生适配,iOS在Info.plist文件中添加权限,Android在Manifest.xml中配置权限等。如果有Flutter实现不了的功能或者因平台特殊要求,还是需要去做原生开发。关于Theme,偏向iOS/Android,都有相应的风格组件,还可以统一设置全局Theme。只需要此时就需要一个Flutter交流群,互相帮助,随时能够解决平台开发者在iOS/Android的原生适配开发问题,希望有号召力的大佬搞一个。
- 与JS原生交互的原理基本相同,无非是建立相同命名的通道,A发送方法名称和参数,B通过回调接收。网上资料很多,自行百度吧,没必要在这里重复造轮子。
本App有导出Excel文件的需求(MacOS与iOS通信方式相同),此时就需要做平台兼容(Platform.isAndroid or Platform.isIOS etc.)。对于Windows平台来说,没有强制规定向用户请求权限,所以直接保存至任何普通文件地址,触发Flutter事件后调用原生SavePanel方法。对于MacOS来说,需要请求User Selected File-Read/Write,而不是下面的Folder权限,并启动原生文件保存窗口。这也是审核被拒的重要原因。
5、macOS App Store上架历程(由于App Store的严格审核机制,一定要把握住权限,连续5天几个版本的拒审你懂的。Android dev可以略过)
- Debug阶段,开启Incoming Connections(Server)是必需的,因为Hot Reload基于此权限,但Release时,一定要关闭,因为App普遍没有作为服务器的类型,会被拒审。
- 避免在任何地方显示“安卓,Android,三星,华为,微软”等苹果在各方面对手的名称,一旦发现,肯定拒审。尤其是笔者开发过的IT新闻类App,上线时需要添加多个“苹果竞争对手”过滤关键词。笔者在填写应用介绍时把自己的技术开发经验写进去,“两年安卓开发经验”直接被拒,只能写为“两年卓卓经验”🤣🤣🤣。
- 由于资料的保密性,为保证通过审核,App采用假注册模式,本地注册存储模拟登录。注册成功后与游客登录后权限相同。
- 凭借笔者多年拒审🤣过审经验,总结一个原则。一定不要和苹果硬怼,让你改你就改,让你怎么改就怎么改。实在不过就使一些障眼法呗。例如当年避免苹果内购也是用尽浑身解数,最终也是只用支付宝支付,微信支付确实没法避免。当年慕课网App可以完全规避,有没有大佬指导如何做到的。
三、Flutter、SwiftUI、Android(鸿蒙)的简单思考
笔者打算将Flutter、iOS、Android、鸿蒙、小程序、快应用、RN等移动开发的未来发展单独介绍分析,奈何篇幅不够,现只做简单阐述,稍后会发布另一篇文章仔细分析。
- Flutter:混合开发,我愿称你为最强!从Flutter2.2性能提升来看,如果性能再有进一步提升,必定成为移动端开发主流,未来可期。稍微吐槽下Dart,比Swift还是差点意思。
- iOS:SwiftUI,又快又靓又牛x。成品效果惊艳,苹果可谓是移动产品和技术的领导者。两个缺点:①支持iOS 13以上,国内开发环境来说,还有一段路要走。②成熟度不够,虽然Swift已经稳定,写起来十分便利,但谁能知道SwiftUI会不会成为下一个SwiftUI。笔者从Swift2.2开始接触,一个版本一个新语言的痛苦至今犹在。
- Android:移动市场最大蛋糕分割者。不限于手机,目前最火的智能汽车市场也是以Android为主,选择它肯定没错。国外Android流畅度接近iOS,但国内安卓你懂的,希望国内推送统一联盟尽快落地实用的标准和机制,避免社会资源的浪费。
- 鸿蒙:Fuchsia没来,鸿蒙它来了。鉴于昨天OPPO公关评论鸿蒙事件,我只能🤫。前一阵与同事闲聊时,谈论过鸿蒙,我只是讲出了一些实事,但他却说我侮辱国产🤣,这么大的罪名,我可担待不起。毕竟用着国产的衣服鞋子桌子椅子食物水,以后可不敢瞎评论。还是希望鸿蒙能够崛起,少吹牛,增加技术强度和提升产品体验。