作者:马坤乐(坤吾)
Flutter 自 2015 年初次亮相以来,经过了多年的发展已经相当成熟,在阿里、美团、拼多多等互联网公司都有广泛的应用。在 ICBU 阿里卖家上 90+% 的新业务使用 Flutter 开发,ICBU 客户端开发组拥有众多的 Flutter 开发人员。
Flutter for Web (FFW) 早期试验版于 2019 年发布,在当时已经有很多感兴趣同学对其进行调研,当时由于刚发布存在诸多问题不适合在生产环境中使用。在今年(2021)三月份,Flutter 2.0 发布,FFW 正式进入 stable 分支。
阿里卖家外贸资讯版块主要使用 Flutter 开发,在本财年的目标中,外贸资讯的App外推广为开源引流的重要一环。App外资讯推广需要一个承载内容Web页面,对该Web页面的要求如下:
由于缺乏前端同学的支持,想要完成此页面只能由 App 端上同学自己投入,经过一定的考虑我们选择了 FFW,理由如下:
经过以上思考,正式开启 FFW 填坑之旅。
目前阿里卖家FFW相关页面已上线,从 FFW 发布至今产物 js 文件大的问题就一直存在,理论上会很影响页面加载体验,实际测试中观察到在 PC、移动设备上加载体验尚可,运行很流畅,相关 Demo 如下:
创建 FFW 工程比较简单,Flutter 切换到 stable 版本,之后运行命令 flutter create xxxProject 进入工程后点击运行一个 Demo 工程便可运行起来。要将 FFW 应用到实际的工程中,需要考虑的是工程的问题和如何融入阿里的体系的问题,如:怎么发布、开发流程如何管控、怎么请求接口等,总结如下:
以上为阿里卖家 FFW 开源引流最小闭环实践中遇到的问题,除此之外 FFW 待建设的问题还有:
接下来是对最小闭环实践中,工程基础问题的出现原因和解决方案的说明。
参考 App 端 Flutter 开发,FFW 中首先要考虑选择 Flutter 的什么版本,其次是考虑如何复用已有的 Flutter 代码。
版本选择问题因 FFW 和 Flutter for App (FFA) 的 Flutter 版本无法统一产生。FFW 需要的 Flutter 版本为 2.0+,而目前我们 App 端内的 Flutter 版本为 1.X+ ,要升级到 2.0+ 版本还需等待不确定的时间。经过一定的考虑目前我们 FFW 和 FFA 选择版本如下:
FFA: hummer/release/v1.22.6.3 -- UC的Hummer分支
FFW: origin/flutter-2.8-candidate.9 -- 官方分支
FFW 不选用 stable 版本是因为在最近刚发布的 iOS 15 上 FFW 页面会因 webGL 问题会卡死,该问题修复方案目前已集成到了candidate版本。(当前最新stable版本为2.10.0,问题已解决)
FFA 代码复用到 FFW 中要考虑的问题分两块:
Dart 代码复用
FFW 需要 Flutter 2.0+ 版本对应的 dart 版本为 2.12,此版本的 dart 引入了空安全 (Sound null safety) 特性。FFA 上使用的 Flutter 版本为 1.+ 版本对应的 dart 还未引入空安全。同时 Flutter 中新老版本 dart 库代码无法混合编译,所以目前对已有 App 端代码库还无法做到无缝复用,只能通过修改已有代码进行复用,代码修改主要的点有:
User? nullableUser;
nullableUser?.toString(); // 空安全,如为空不会出现NPE
nullableUser!.toString(); // 强制指定非空,如为空会报错
/// 老版本
User({
@required this.name,
@required this.age,
});
/// 新版本
User({
required this.name,
required this.age,
});
低版本代码经过这三步修改后基本可在新版本编译通过,除此之外还会有部分 API 由于版本升级产生变更,也需要相应的修改,如:
/// 老版本
typedef ValueWidgetBuilder
= Widget Function(BuildContext context, T value, Widget child);
/// 新版本
typedef ValueWidgetBuilder
= Widget Function(BuildContext context, T value, Widget? child);
在 API 变更中这类问题占大多数,修改起来较简单。另外还有一类改动,如在抽象类 TextSelectionControls中,handleCut等方法参数的个数发生了变更:
/// 老版本
void handleCut(TextSelectionDelegate delegate) {...}
/// 新版本
void handleCut(TextSelectionDelegate delegate,
ClipboardStatusNotifier? clipboardStatus)