作者简介
Kui,携程移动端高级软件工程师,专注于移动端开发,热衷于移动端跨平台技术的研究和实践。
一、前言
距离Flutter正式发布已经3年了,国内各大互联网公司都有相继使用,携程今年也在许多业务中使用了Flutter进行开发。
Trip.com是一款面向海外用户的App,从年中开始便将卖点页、预定页等页面全量转为Flutter,随之而来的便是代码质量管理的问题。由于篇幅有限,本文将从静态代码检测、空安全、单元测试这几个部分来介绍Trip.com在Flutter业务迭代中提高代码质量做的一些努力。
二、空安全&静态代码检测
空错误是在开发中出现频率较高且通常很难被发现的一类错误。现在越来越多的语言支持空安全。Dart 自2.12版本之后,也支持了稳定的空安全声明,可以在编译期就避免空错误。
2.1 空安全语法
下面整理了常用的空安全语法。
int? aNullableInt = null; //可空声明
late int lateInt; //延迟声明
int value = a ?? b; //如果a为空则执行b
int value = aNullableInt!; //非空操作符
cat?.mouth.eat(); //如果为空不执行后面的方法
func(String a, {required String b, String? c}){} //必传参数和可空参数
List<String> //包含非空字符串的非空列表
List<String>? //包含非空字符串的可空列表
List<String?> //包含可空字符串的非空列表
List<String?>? //包含可空字符串的可空列表
var map = <String, int?>{'test': 1}; //未指定类型时{}是set类型
Function(String a)? func;
func("2"); // error
func?.call("2"); //ok
2.2 空安全迁移
由于在Dart 2.12之前,我们便在项目中集成了Flutter,为了支持空安全,首先得将项目迁移到Dart 2.12版本。
可能存在的问题
1)依赖库不支持空安全
只有在所有的依赖都支持空安全的情况下,才可以在健全的空安全下运行项目,所以需要保证所有依赖库都支持空安全,不过现在大部分第三方库都是支持的。
2)代码量大
不需要一次性迁移完成,指定Dart版本号渐进迁移,避免业务修改Merge代码的问题。下文会有空安全迁移的推荐步骤。
3)契约的更新
契约通常文件很多,一般使用脚本批量生成,如果要修改生成的规则、字段是否可空,尽量在空安全迁移之前或者之后统一处理,防止某些字段的空警告消失。尽量避免给List.add()
这种集合操作的方法加?
可空操作符。
4)Migrate导致的错误
Migrate是官方提供用来迁移空安全的工具,但是在使用的过程中却存在许多坑点。
不合理的强制转换。将可空强转为非空类型。如
Future<T>
强转成FutureOr<T?>
。注意Map
和Map<String, dynamic>
。Object
、Object?
、dynamic
,{}与<dynamic, dynamic>{}
的区别。
无法正确的识别可空类型,可能也与原始代码的实现方式有关。会增加代码判空复杂度。
无理的非空。
一些基础库的泛型没标识非空,无法正常加
?
标识符。
还会有一些遗留问题,代码上标识为错误和黄底警告,比如多余的
?
操作符等,都需要手动修改。
5)analysis_options文件中exclude的文件会被Migrate工具忽略,同时也会被空安全语法的代码检测忽略。
6)空安全迁移后还有type 'Null' is not a subtype of type 'xxx'
、Null check operator used on a null value
错误。
迁移完空安全后可以免大部分空错误,还会存在一小部分空错误,这是由于!
操作符不合理的使用,dymamic
隐式转换等原因导致的,需要避免使用强制非空以及静态代码扫描来检测。
空安全迁移的推荐步骤
1)flutter pub outdated --mode=null-safety
保证所有库都支持,flutter pub upgrade --null-safety
升级所有依赖库到支持版本。
2)dart migrate --skip-import-check
打开migrate,反选所有文件,点击apply,会自动的升级pubspec.yaml版本并给所有文件加上@dart=2.9
注释。
3)自底向上的适配项目中的文件。将文件的@dart=2.9
注释删除会出现很多空安全错误和警告,警告也需要修改。(如果要用Migrate修改一定要对检查每个改动)
迁移顺序:公共库 → 业务基础库、Utils、Model → ViewModel → Widget → main.dart
4)main.dart的@dart=2.9
移除后,项目将以健全的空安全模式运行。
2.3 配置静态代码扫描
静态代码扫描