背景

雪球目前有3个App,雪球、蛋卷基金、雪盈证券,每个app又有各种版本比如feat、rc、sep等等。在开发阶段打出包后,如何能让开发同学测试同学,设计同学,产品经理等快速找到,并方便的安装到手机上?很旧之前是有个网页提供下载,但是年久失修,没有分类不支持搜索,安装也不太方便。所以就有了重构雪球应用广场的需求。

整个重构包括打包优化,后台接口,与前台界面。这里主要介绍前台界面。

19年7月,开始做这个app,大概一个月的时间,从入门学习到公司内部上线,完成3端开发,Android iOS web。也体现出了flutter的优势。

app样子如下:

Flutter

最近flutter异常火热,Flutter是Google开源的跨端UI工具包,能一套代码提供ios android web三端的应用。我们基础研发平台也一直想在雪球内部试用下,当个快速试错者,帮助大家快速踩坑。正好借这个重构的契机,所以选取了flutter的跨端开发框架。结果证明确实很香,没让我们失望。

我们看这个图,除了手机端和web端之外,flutter也是支持电脑端应用和嵌入式应用的开发,除了想跟React Native PK 之外,还想跟桌面端开发框架electron及嵌入式C语言PK下。另外也是谷歌另一个新系统Fuchsia的主要开发方式。

Flutter的优点

为什么这么多厂商使用flutter,根据我近段时间开发flutter的新得体会,列出几点。

1.跨平台,渲染一致,一套代码,多端使用

首先是他的跨平台真的做的很好,不同平台渲染一致性很高,真正做到了一套代码多端运行,而不是像之前的跨端方案 一套代码 多端修改适配。

这个图是雪糕应用在不同系统下的样子,UI几乎无差别,开发的时候不需要关注平台,android和ios的适配基本没花多长时间。

当时令我眼前一亮的一点是他竟然能把这种一致性延申到了web上。因为当时开发的时候,flutter还是1.7版本,官方还不支持web h5, 所以对能否做出web版没抱任何希望,当时想的是单独开发一套出h5版, 后来客户端开发完后,尝试性用内测分支flutter_web 适配了下,发现还是可以用的。UI体验与客户端差不多,除了有些bug与卡顿。所以既然web也能支持,我想他们之后说的桌面端和嵌入式也很期待。现在1.9 版本 web已经支持了,不用像我开发时,做大量的安装和适配工作了。

注:在今年(2020)我们桌面版选型时,我试用了下flutter桌面版的生成,只需升级下flutter版本,没有任何代码改动就打出了flutter桌面版,界面效果很优秀。文章如下链接。

https://blog.dappwind.com/2020/03/17

2.开发效率高

之前客户端同学一直羡慕我们前端同学开发效率高,代码改动后只要保存下,就能直接在界面上看到效果。 flutter也支持热重载这个功能。可以提高开发效率。

另外就是编辑器的代码补全,每个组件代码前面都有个一个电灯泡,可以直接选择添加哪些代码片段,并不是简单的代码很智能,减少了手动敲重复代码,提高开发效率。

3.UI可深度定制,

第三点就是UI可深度定制,性能媲美原生应用,可以通过下面这个视频看一下。

Flutter 的优点是怎么实现的

为什么会有这些优点,我们简单看一下flutter的框架,底层不调用原生组件,都是用skia引擎画出,所以能有很高的跨平台一致性,UI深度定制性,性能好。

Dart语言

而开发热重载则是Dart语言的一个优势。Dart支持两种模式

JIT (Just-In-Time - 实时编译) 解释模式

AOT (Ahead-Of-Time - 预先编译) 编译模式

debug模式下使用的是解释模式,跟js一样,所以支持热重载,发布打包时,会先编译,性能比js好得多。所以也是flutter性能媲美原生的原因。

1.Dart 的性能更好。

Dart在 JIT模式下,速度与 JavaScript基本持平。但是 Dart支持 AOT,当以 AOT模式运行时,JavaScript便远远追不上了。速度的提升对高帧率下的视图数据计算很有帮助。

2.Native Binding。

在 Android上,v8的 Native Binding可以很好地实现,但是 iOS上的 JavaScriptCore不可以,所以如果使用 JavaScript,Flutter 基础框架的代码模式就很难统一了。而 Dart的 Native Binding可以很好地通过 Dart Lib实现。

3.Dart是类型安全的语言,拥有完善的包管理和诸多特性。

Google召集了如此多个编程语言界的设计专家开发出这样一门语言,旨在取代 JavaScript,所以 Fuchsia OS内置了 Dart。Dart可以作为 embedded lib嵌入应用,而不用只能随着系统升级才能获得更新,这也是优势之一。

4.Dart 2.5 支持了ML代码补全,调用C语言等功能

Flutter项目结构

Flutter编程思想

一切皆 Widget
Widget 是 Flutter 应用用户界面的基本构建单元,每个 widget 都与最终的用户界面的展示紧密相关。不同于其他框架和平台 —— 将视图 (views)、视图控制器 (view controllers)、布局 (layouts) 等其他属性分开,Flutter 拥有统一的对象模型:widget。
一个 widget 可以定义:

  • 一个结构元素(比如一个按钮或者菜单)
  • 一个风格元素(比如一个字体或者配色方案)
  • 布局的一个方面(比如 padding)
  • 等等……

是声明式UI,一切都是widget, 一切都是小组件。
触摸操作也是组件。

下面是一个widget组件的例子,雪糕里有很多不同的app图标,大小,种类,有无边框会有些区别。所以封装成了组件,暴露参数 为 app内容,宽度,有无边框。

边框和背景图为最外层widget,里面有个文字的widegt.

这就是全部的icon组件代码。

Flutter 插件/包管理

类似前端 npm , 我问了下客户端的同事,引用第三方的包时,需要直接上github上搜索,并手动下载。比较麻烦,这个pub.dev就采取了前端开发包管理的方式,是个单独的网站,直接搜索,然后添加到pubspec.yaml里面就行。会自动安装。

雪糕用到的插件如下:

以分享插件为例

雪糕主要功能及其实现方式

1.顶部动画及导航栏 - NestedScrollView / headerSliverBuilder / SliverPersistentHeaderDelegate 画出

2.网络请求及页面间数据通信 - http / JSON / event_bus

3.应用内下载及安装 - android: flutter_downloader & path_provider / iOS: url_launcher / web: dart open

4.搜索功能 - SearchDelegate

5.页面框架 - Scaffold / ListView

6.封装组件 - 列表组件 / 卡片组件 / icon组件

7.判断系统环境 - device_info

这个地方罗列了一些主要的效果与功能,我们选取两个来具体说下,

首先是顶部动画和导航栏

我们的设计师设计了非常好看的顶部效果和导航栏样式,使用flutter自带的materal design的UI组件不能满足需求。导航栏背景是个可以动的图片背景,一直延伸到系统状态栏。并且顶部滑动效果,背景多个元素是差速移动的,另外,卡片是压在背景上方,上滑到某一位置后,卡片开始被收到状态栏背景下方。并且还要适配机型与屏幕。

由于刚开始用flutter, 看到设计稿时感觉flutter做不到这个效果,完全没有头绪,后来弄了好几天,换了3个方案,基本实现了这个动效

没有用flutter提供的appbar 相当于自己画了一个。因为flutter有stack层叠的概念,将差速移动的各个元素放在不同图层,每个的定位都加一个运动曲线公式。

代码大概结构如下 stack 元素 堆叠了 5个 positioned 图层

开发时还遇到了 listview滚动与appbanner 滚动事件冲突的问题,也是花费了些时间解决,又遇到类似问题的同学可以参考下git代码。

另外介绍下雪糕内安装app的实现方式

安装app ios 与 android很不一样,ios安装直接safari打开一个plist链接。所以用到了url_launcher 这个插件,他会打开链接进而进行安装。

安卓需要先下载再安装,安卓下载涉及到队列数据库等等设置,可以参考flutter_downloader 的说明文档。

打包

RN对比

RN采用原生组件,与系统内的一致性强,但是ios跟android有些差异

第三方包活跃

RN list有些性能问题

Flutter遇到的问题

1.SliverAppBar 与 scroll controler 冲突

SliverAppBar will not collapse when ListView is scrolled
来自 https://github.com/flutter/flutter/issues/26243

https://stackoverflow.com/questions/46817189/sliverappbar-and-listview-with-controller/46853315#46853315

Listview tabbarview 在 customscrollview 中
List 滚动不会引起 appbar滚动

解决:

NestedScrollView 中的 headerSliverBuilder

NestedScrollView 不只可以用 SliverAppBar 还可以接受 SliverPersistentHeader

SliverPersistentHeader 中可处理滑动,结合NotificationListener可实现滚动效果。

2.Future 异步 需在页面切换走后 停止,否则setState报错

https://stackoverflow.com/questions/49340116/setstate-called-after-dispose

1
2
3
4
5
if (this.mounted){
setState((){
//Your state change code goes here
});
}

3.Text hero

颜色会变红,字体会变,会换行

https://github.com/flutter/flutter/issues/10246
https://github.com/flutter/flutter/issues/30647

Text hero
设置颜色、设置字体、设置宽度,依旧效果不太好。