在大略看完了Flutter的官方文档后, 我开始尝试做一个完整的应用Demo. 抱着初次开发不要给自己挖坑的前提, 我选择了一个简单而实用的空气质量项目: Aireport. 这是我之前自己开发的一个iOS原生App, 因为是给自己用, 因此项目很简单, 几百行代码. 同时做一款Flutter版的Aireport也能够探寻两者具体的差异点.
首先说一下Flutter应用的开发流程.
第一步自然是新建工程.根据我的《闭坑指南》,编辑器还是强烈建议使用Google自家的Android Studio, 无论是插件支持程度还是速度上都要好上不少(和VS Code相比).
然后是引入第三方库.这里我使用了最基本的几个库, 包含有图标库、网络库、json转model以及本地存储.
cupertino_icons
http
json_annotation
shared_preferences
Flutter目前还没有提供一个非常好的json转model的方案, json_annotation是官方目前推荐的“最方便”的model库, 但还是需要通过脚本的方式生成json转model类. 这显然没有动态转换但效率高.
我在网络层和UI层之间做了一个简单的存储层, 用于缓存网络请求的数据.
接下来就是正式开始编写代码咯. 作为一个略懂前端的iOS开发者, Dart本身的语法上手速度还是很快的, 不过这个层层嵌套的结构实在是太恶心了, Android Studio自带的代码格式化似似乎也不太稳定.
其次是Flutter的布局. 大体的布局和前端布局框架很相像, 但它将功能“过分组件化”了, 原本DIV都有都Padding、Center、Align属性全部Widget化, 因此如果你的布局比较复杂, 很可能需要多个布局Widget嵌套来实现.
看到这样的代码我的内心是崩溃的💔
如果你是原生开发者, 你会很自然的认为TableView(ListView)、CollectionView(GridView)是一个UI控件,包含了对Cell的展示、控制、点击事件的处理. 而Flutter将这个控件拆分成了多个组件,ListView和GridView仅仅承担布局的工作, 点击事件等需要通过别的控件来实现.
可以这样理解, Flutter尽可能的将UI、布局、功能进行分解Widget化, 我还不确定这样做的目的是出于性能优化的考虑还是别的原因. 当然适当的组件化是好的, 能够使控件足够灵活, 也能对性能有适当提升. 但如此小颗粒度的分解目前看来也带来了一些使用上的问题, 包括上面提到的多层嵌套.
当你的代码写的差不多的时候, 就要开始调试代码. 调试Flutter的过程是愉悦的, 任何更改后按下保存快捷键你就能立刻看到修改后的样子(理想情况下).令人欣慰的是Android Studio还提供了足够强大的调试工具来帮助我们进行纠错, 在这点上VS Code能做的就很少了, 至少目前来说.
如果需要对UI进行调试, 可以尝试加入
import 'package:flutter/rendering.dart';
debugPaintSizeEnabled= true;
通过辅助线我们可以快速定位控件的布局
Android Studio还提供了inspector视图, 包含了Widget树状图以及了Widget的各种属性, 类似于浏览器的调试器. 不过目前的视图还比较混乱, 应该是把底层的所有Widget无差别展示出来, 对于感兴趣底层实现的同学可以研究一下.
项目开发完成, 接下来就是打包发布了. Flutter有两种运行方式, debug模式提供了热更新等功能方便我们快速开发, 但运行效率低下. 而release模式剔除了无用的功能并以AOT方式运行, 效率更高.
flutter build ios
构建release的iOS版本, 然后去Xcode中设置app的Icon、启动页、版本号、证书等等, 就如同一般的原生应用一样. 注意, 如果你的app中使用了非HTTPS协议还是需要在Plist中对安全规则进行修改, 地理位置、相机权限也是同理.
至此, 我的Flutter iOS项目已经开发完成了. App Store2天左右就通过了审核, 显然目前Apple对于Flutter的态度还是和善的, 但未来的政策还是未知.
在开发这个项目的过程中, 我遇到了几个问题, 也想跟大家分享一下.
首先是布局.
这个页面其实相当简单, 上面是一个搜索框, 下面是一个ListView. 但我始终无法通过简单的布局实现这个效果. 一开始我本能但使用Column来放置两个控件, 但报错, 提示的大意是不能在一个可以滚动的组件内部套另一个可以滚动的组件, 因为无法确定子组件的高度. Column的布局其实类似iOS中的ScrollerView, 他的内部填充高度是无限的, 和屏幕高度无关. 可以给ListView添加高度解决问题, 但我又无法计算当前ListView的具体高度(方法是有, 但我觉得太复杂了, 还要区分X这种特殊机型).
最后我使用了Stack, 将搜索框悬浮在ListView之上, 再给ListView套一个Container设定到上边距的距离, 最终实现了这个效果. 虽然最终的效果还可以, 但我觉得这样但布局明显还不够优雅, 阅读起来难度也很大. 如果有更好但解决办法请告诉我.
其次是尚有很多系统层级的功能无法实现. 比如StatusBar的颜色, 我希望的是首页是白色字体, 搜索页是黑色字体. 但目前似乎只能设置一种固定的颜色. 尝试使用SystemChrome强制设置StatusBar的颜色,但跳转到搜索页颜色又会变为黑色, 返回时不能自动变为白色. 尝试寻找类似于ViewWillAppear这样的事件无果, 暂时只能通过后一个页面的回调来触发修改颜色的代码.
Notification也是一个暂时无法实现的功能.目前官方已经提供了firebase_messaging插件用于收取推送消息, 但无法发送本地推送, 也就无法实现小圆点的功能.当然我们还是可以通过调用原生方法实现.
总结一下.
从UI层面, Flutter由于提供了一套脱离特定平台的自有渲染引擎, 几乎能够实现任何你想要达到的效果. 但在这之前你可能需要好好研究一下它的布局Widget, 然后暂时(可能永远)忍耐一下多层嵌套的代码风格. 当然适当将自己的页面组件化也是一个好的方法.
逻辑层面, 官方已经推出了数量可观的基础库, 处理基本的事务没有特别大的问题. 但我们还是希望有大量好用的第三方库出现, 丰富Flutter的开发生态.
下一篇会做一个iOS原生应用和Flutter的对比评测.
最后放上项目GitHub地址: https://github.com/jihongboo/Aireport_Flutter
现已上架AppStore: https://itunes.apple.com/cn/app/aireport-flutter/id1390259423?mt=8