Flutter开发项目的感受
一直从事移动端开发(iOS和Android),可能是对两端开发都熟悉的缘故,本人对跨平台一直是持谨慎甚至可以说是质疑态度的,因为真正做到跨平台很难。
这两年Google推出的Flutter一直也有去了解,但都是些零零散散的知识点,没有一个整体宏观的感受,最近就用项目来验证了一下开发细节和流程。
1. 开发思想上的差异
iOS和Android目前的主流技术还是命令式编程,而Flutter借鉴自React属于声明式编程。
对于前端小伙伴来说Flutter的声明式编程很好理解,毕竟和React和Vue等框架都是类似的,我将声明式编程简单的理解为数据驱动的开发模式。
我们用一个列表的案例来体会一下:
iOS
UITableView通过从UITableViewDataSource
和UITableViewDelegate
获取数据和样式展示列表。
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _dataArray ? _dataArray.count : 0;
}
当_dataArray
数据改变后,UITableView是没法直接刷新的,需要调用手动调用UITableView的reloadData
等方法:
[self.tableView reloadData];
Android
RecyclerView或者ListView通过Adapter
获取数据进行列表的展示:
@Override
public final void onBindViewHolder(@NonNull VH holder, int position) {
//...
}
@NonNull
@Override
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return onCreateHolder(parent, viewType);
}
@Override
public int getItemCount() {
return _dataList.size();
}
当_dataList
数据改变后,RecyclerView是没法直接刷新的,需要调用手动调用Adapter的notifyDataSetChanged
等方法:
mAdapter.notifyDataSetChanged();
FLutter
ListView通过itemBuilder
或者children
获取Widget进行展示。
ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: list.length,
physics: ClampingScrollPhysics(),
itemBuilder: (ctx, index) {
return _buildTopItem(list[index])},
)
当list
发生改变后,ListView会根据新的数据进行列表的刷新,无需显示调用刷新方法。
Vue
Vue使用的是模板语法,使用v-for将数据items
展示为相应的HTML元素列表。
<div v-for="item in items" v-bind:key="item.id">
<div>{
{item.title}}</div>
</div>
当items
发生改变后,HTML元素列表会自动刷新,无需显示调用刷新方法。
- Vue中有
v-bind:key
, Flutter中Widget的构造函数中也有key
,其实都是借鉴于React的虚拟DOM,这个Key是起到diff时候进行对比来决定是否需要刷新的作用。
重点注意: iOS可以通过KVO将UI和数据进行绑定;而Google现在主推的JetPack也有DataBinding可以进行双向绑定。所以并不是iOS和Android开发没法进行声明式编程的开发,只是主流开发不是而已。
如果你有在原生开发上使用的有这些数据绑定的技术,那这里提到的命令式编程和于声明式编程的差异就可以跳过了。
2. 代码结构上的差异
现在的原生开发都比较青睐MVVM的架构,所以代码分层上来说是很清晰的。
但是我开发Flutter的感觉是代码的易读性和可维护性差一些。
Flutter中一切皆为Widget,Widget可以是展示的元素,例如:ListView
,GridView
, Image
等;Widget可以是样式,例如:TextStyle
, Theme
,Opacity
等;Widget也可以是事件,例如:GestureDetector
,AbsorbPointer
等;Widget也可以是数据和界面的组合体,例如:FutureBuilder
,StreamBuilder
等;总之,你想到的几乎所有的都是Widget。
由于借鉴了虚拟DOM来提高渲染效率,这些Widget组成了一个层层嵌套的树状结构,所以就形成了网友们调侃的嵌套地狱模式。一个界面有一个根Widget,然后所有的内容都在这根Widget下一步步嵌套下去。
例如:要实现如下的一个按钮,点击按钮跳转到播放页面
理想的情况下需要嵌套GestureDetector、Container和Text三层Widget,分别处理手势,控制大小和圆角、背景颜色,文字和样式。
// 点击的Widget
GestureDetector(
onTap: () { print("跳转到播放页面"); },
// 子Container
child: Container(
width: 196.rpx,
height: 50.rpx,
decoration: BoxDecoration(
color: FBTheme.redColor,
borderRadius:
BorderRadius.circular(50.rpx)),
alignment: Alignment.center,
// 子Container
child: Text(
"立即播放",
style: TextStyle(
color: Colors.white, fontSize: 30.rpx),
),
),
)
有的小伙伴可能会抽提Widget的方式进行代码复用和减少单个文件的代码量,但是这样又会增加数据传递和事件回调的复杂度。
3. 开发速度上的差异
Flutter在开发速度还是很快的,他的快速体现在两个方面。
- 一套代码可以实现跨平台的功能,所以相比开发iOS和Android两套代码速度上就快了很多。
- 开发Flutter有Hot Reload的功能,修改代码后保存一下代码就能看到修改后的效果。
这个功能iOS的小伙伴还是很羡慕的,每次修改iOS代码,都需要重新运行一下程序才能看到效果,即使熟悉各种LLDB调试技巧也是个很耗时的流程。而Android Studio推出的ApplyChange的功能还是很友好的。
4. 开发难度上的差异
iOS和Android的原生开发的难度在需要熟悉对应平台的API,如果需要两个平台都开发那就接近双倍的工作量和难度了。原生开发实现各种需求功能还是没有太大的问题,对应平台的限制除外。
Flutter开发需要熟悉Flutter的API,熟悉一套就可以跨平台开发了。
- Flutter的文档和API还是非常详细的,甚至每个Widget都会有使用案例,这个对开发者是非常友好的。
- 此外,Flutter封装的Widget非常丰富,甚至有些冗余。不需要寻求原生支持的情况下就能够满足绝大部分需求。
Flutter的难度在于开发者还是需要有原生开发的一些基础知识,这样在遇到问题时候能够很快速的解决问题,或者需要原生支持的时候写一些插件支持开发。
5. 执行效率上的差异
Flutter其实就是一个UI框架,基于Skia的图像渲染引擎进行绘制。由于Android本来就是基于Skia这个引擎,所以Flutter开发的应用在Android平台上和原生开发的性能相差不大。
Flutter在iOS平台上其实是绕过了Core Text渲染引擎,而直接使用Skia引擎进行图像绘制,所以性能上也相差不大。但是由于需要引入Skia引擎,所以iOS包的体积会加大。
总结:
总体来说Flutter的开发体验和使用体验都还是很不错的。只要不触碰到苹果的一些利益和规则,这个跨平台方案能够有很大的发展空间。
Flutter实战项目介绍
我用Flutter撸了一个几乎完整的播放器App,一起学习,共同进步。
使用的技术包含但不限于:
- 框架搭建
- 网络请求
- 应用主题
- 动画实现
- 事件总线
- 页面路由跳转
- 视频播放
- 数据库的存储
- SharePreference存储
- 图片的适配
- Iconfont的使用