APP对Flutter 2.0空安全的适配

2021年3月4日凌晨,Flutter 2 正式发布,除了增强了对于多平台的适配(包括:web、windows、Linux)外,很重要的一点就是Flutter 2.0中使用的编程语言Dart的版本更新为 2.12,并支持健全的空安全。我们APP在1.0.4版本就升级了Flutter 2.0,并完成了对空安全的适配,本文在这里希望和大家分享下空安全适配的实践过程和踩坑,欢迎一起交流探讨。

理解空安全

空安全(Sound null safety)特性并不是 Dart 独有的,Kotlin, Rust, C#, Swift 等语言都有此特性。空安全默认代码中的所有的类型都是非空的,并且使用了特定的静态检查和编译优化,使得在运行时,可以保证不会发生空指针引用和解引用(null-dereference)错误,因为这会在编译时就会发现并解决,增加了代码的健壮性。

让我们来看下面这个例子:

// Without null safety:

bool isEmpty(String string) => string.length == 0;

main() {

  isEmpty(null);

}

如果你的Dart 程序时并未使用空安全,它将在调用 .length 时抛出 NoSuchMethodError 异常。 如果可以让静态类型检查器,使得诸如在可能为 null 的值上调用 .length 这样的错误能提前能被检测到,就可以提升APP的稳定性和用户的体验。

值得注意的是,我们的目标并不是 消除 null。相反,可以表示一个 空缺 的值是十分有用的。 null 并不糟糕,糟糕的是 它在你意想不到的地方出现,最终引发问题。

因此,对于空安全而言,我们的目标是让你对代码中的 null 可见且可控,并且确保它不会传递至某些位置从而引发崩溃。

非空和可空类型

在空安全推出之前,静态类型系统允许所有类型的表达式中的每一处都可以有 null。从类型理论的角度来说,Null 类型被看作是所有类型的子类

但是 null 值并没有它们定义的任何一个方法和属性。所以当 null 传递至其他类型的表达式时,任何操作都有可能失败。这就是空引用的症结所在——所有错误都来源于尝试在 null 上查找一个不存在的方法或属性。

空安全通过修改了类型的层级结构,从根源上解决了这个问题。 Null 类型仍然存在,但它不再是所有类型的子类。

image.png

这样,所有的类都分成两部分,一部分为可空类型,一种就是非可空类型,Null仍然是任何可空类型的子类。

image.png

使用可空类型

我们将所有的可空类型作为基础类型的超类。你也可以将 null 传递给一个可空的类型,即 Null 也是任何可空类型的子类:

Flutter APP对空安全的适配步骤

官方文档中对于APP的迁移过程有如下的步骤建议:

  1. 等待 你依赖的 package 迁移完成。
  2. 迁移 你的 package 的代码,最好使用交互式的迁移工具。
  3. 静态分析 package 的代码。
  4. 测试 你的代码,确保可用。
  5. 如果你已经在 pub.flutter-io.cn 发布了你的 package,可以将迁移完成的空安全版本以 预发布 版本进行 发布

但是有时候并非你使用的所有的package都能及时完成迁移,而且你迁移的越晚,需要的成本越大。而且很多package的新版都必须要求你的工程中使用空安全,因此,我们就必须采用一种方式,来实现非健全的空安全。然后随着所有的package完成迁移,你就可以从非健全的空安全转换为健全的空安全。

  1. 非健全的空安全

// @dart=2.9 是Dart语言的一个语法糖,将它放到任何一个Dart文件的第一行就代表着,这个Dart文件指定 Dart 2.9 的语言版本进行静态分析,可以减少未迁移的分析错误,保证你的代码可以编译通过。

首先,编辑 package 的 pubspec.yaml 文件,将最低 SDK 版本设置到 2.12.0:

environment:

  sdk: '>=2.12.0 <3.0.0'

其次,新建一个app_main.dart文件,将你原来在main.dart文件中的代码转移到这个文件,并修改代码中的main方法,名称为app_main.

最后,在原来的main.dart文件中,加入如下代码:

// @dart=2.9

import 'app_main.dart';

void main(){

  appmain();

}

这就保证了你的代码可以在部分支持空安全的情况下仍然可以顺利的运行。

2. 迁移

针对迁移,你有两个选项可以选择:

· 使用迁移工具,它可以帮你处理大多数可推导的变更。

· 自己动手,丰衣足食。

使用迁移工具

迁移工具会带上一个非空安全的 package ,将它转换至空安全。你可以先在代码中添加 提示标记 来引导迁移工具的转换。

开始转换前,请做好如下的准备:

· 使用 Dart SDK 的最新版本。

· 运行 dart pub outdated --mode=null-safety 以确保所有依赖为最新且空安全。

在包含 pubspec.yaml 的目录下,执行 dart migrate 命令,启动迁移工具。

$ dart migrate

如果你的 package 可以进行迁移,工具会输出类似以下的内容:

View the migration suggestions by visiting:

  http://127.0.0.1:60278/Users/you/project/mypkg.console-simple?authToken=Xfz0jvpyeMI%3D

使用 Chrome 浏览器访问 URL,你可以看到一个交互式的界面,引导你进行迁移:

图片 1.png

你可以在工具中看到其推断的所有变量和类型注解。例如,在上面的截图中,工具推断第一行的 ints 列表元素可能为空,所以应该变为 int?(先前为 int)。

手动迁移

如果你不想使用迁移工具,你也可以手动进行迁移。

我们推荐你 优先迁移最下层的库 —— 指的是没有导入其他 package 的库。接着迁移直接依赖了下层库的依赖库。最后再迁移依赖项最多的库。

举个例子,假设你的 lib/src/util.dart 导入了其他(空安全)的 package 和核心库,但它没有包含任何 import <本地路径> 的引用。那么你应当优先考虑迁移 util.dart,然后迁移依赖了 util.dart 的文件。如果有一些循环引用的库(例如 A 引用了 B,B 引用了 C,C 引用了 A),建议同时对它们进行迁移。

手动对 package 进行迁移时,请参考以下步骤:

  1. 编辑 package 的 pubspec.yaml 文件,将最低 SDK 版本设置到 2.12.0
    environment:
        sdk: '>=2.12.0 <3.0.0'

2.重新生成 package 的配置文件:

    $ dart pub get

在版本最低是 2.12.0-0 的 SDK 上运行 dart pub get 时,会将每个 package 的默认 SDK 版本设定为 2.12,并且默认它们已经迁移至空安全。

  1. 在你的 IDE 上打开package 。 你也许会看到很多错误,没关系,让我们继续。

  2. 利用分析器来辨析静态错误,逐个迁移 Dart 文件。按需添加 ?!required 以及 late 来消除静态错误。

  3. 迁移后的代码调整

自动迁移虽然可以批量帮助你做迁移,但是其中很多自动调整的代码是需要你自己根据业务来进行调整的。

  1. 类中未初始化的属性被默认设置为可空类型

但是有时候,我们可能会在后面的代码中,进行赋值,并且为非空,这时候就应该将该属性声明为late。
late 关键字主要用于延迟初始化。初始化主要分为两种:声明处默认值初始化和延迟初始化。但是并不是所有场景都合适使用声明处默认值初始化,这时候就需要使用late,但是当使用late变量时,一定要确保在使用之前要初始化,否则抛出异常。

  1. 删除对数据的空判断

以前很多代码的空判断可能就需要删除了,因为很多类型设置为非空,就不需要判断。一些可能为空的判断就需要随着修改。

比如:对于bool? login类型的代码,我们以前可能需要设置为

if(login),那就需要修改为,if(login == true)

  1. 有时候某些 await 语法会被强行增加 as FutureOr ,如果你不需要改为原来的声明就可以。

  2. 有时候一些方法定义也会被强行修改,比如 redux 相关的这些修改可能也会影响运行问题,所以只需要把 as 部分去除就可以了。

  3. 而比如这类方法报错,一般就是提供的参数和使用参数对应不上,只需要添加上 ? 即可修复

  4. 完全的空安全

当完成你的工程代码的空安全的迁移以后,就需要查看哪些库没有适配空安全。

dart pub outdated 

会列出所有的未支持的库,这时候你有三种选择:

  1. 等待代码库的开发发布新版本

  2. 如果有可以替代的库,替换已经适配空安全的库

  3. 如果是开源库,clone库,自己进行空安全的适配

当所有的库,都修改完之后,就可以将我们的app.dart文件复原为原来的文件,并删除app_main.dart文件。就完成了整个的迁移过程。

  1. 总结

    使用Flutter 2.0并不意味着必须适配空安全。空安全是Dart 2.12增加的新功能,你可以使用Flutter 2.0,但是使用Dart 2.9的语法规则和特性。但是由于Flutter的开发很依赖于第三方库,而第三方库在适配空安全之后,就不再维护非空安全的版本,因此尽早的迁移到空安全,除了提升APP的稳定性有好多,也有利于使用新的第三方库进行开发,包括第三方库的bug修复和新功能的支持。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容