迁移到 null safety
本页介绍如何以及何时将代码迁移到 null safety。以下是迁移你拥有的每个包的基本步骤
- 等待你依赖的包完成迁移。
- 迁移你的包代码,最好使用交互式迁移工具。
- 静态分析你的包代码。
- 测试以确保你的更改有效。
- 如果包已在 pub.dev 上,则以预发布 (prerelease) 版本发布支持 null safety 的版本。
要非正式地了解使用迁移工具的体验,请观看此视频
1. 等待迁移
#我们强烈建议按顺序迁移代码,首先迁移依赖关系图的叶子节点。例如,如果包 C 依赖于包 B,而包 B 依赖于包 A,那么应首先将 A 迁移到 null safety,然后是 B,最后是 C。
尽管你可以在依赖项支持 null safety 之前进行迁移,但当你的依赖项迁移时,你可能需要更改代码。例如,如果你预测函数将接受一个可空参数,但包将其迁移为不可空,那么传递一个可空参数就会成为编译错误。
本节介绍如何借助 null safety 模式下的 dart pub outdated
命令检查和更新包的依赖项。说明假定你的代码处于版本控制之下,以便你可以轻松撤销任何更改。
切换到 Dart 2.19.6 版本
#切换到 Dart SDK 的 2.19.6 版本。此版本包含在 Flutter 3.7.12 SDK 中。
检查你是否安装了 Dart 2.19.6
dart --version
Dart SDK version: 2.19.6
检查依赖状态
#使用以下命令获取包依赖项的迁移状态
dart pub outdated --mode=null-safety
如果输出显示所有包都支持 null safety,那么你就可以开始迁移了。否则,请使用Resolvable列查找支持 null safety 的版本(如果存在)。
以下是简单包的输出示例。每个包的绿色勾选版本都支持 null safety
输出显示,该包的所有依赖项都有可解析的、支持 null safety 的预发布版本。
如果你的包的任何依赖项尚未支持 null safety,我们鼓励你联系包所有者。你可以在 pub.dev 上的包页面上找到联系方式。
更新依赖项
#在迁移你的包代码之前,将其依赖项更新到支持 null safety 的版本
运行
dart pub upgrade --null-safety
将依赖项升级到支持 null safety 的最新版本。注意:此命令会更改你的pubspec.yaml
文件。运行
dart pub get
。
2. 迁移
#你的代码实现 null safety 所需的大多数更改都很容易预测。例如,如果变量可以为 null
,其类型需要添加 ?
后缀。如果命名参数不应可空,请将其标记为 required
或为其指定默认值。
你有两种迁移选项
使用迁移工具
#迁移工具接收一个不支持 null safety 的 Dart 代码包,并将其转换为支持 null safety 的代码。你可以通过在 Dart 代码中添加提示标记来指导工具的转换。
在启动工具之前,确保你已准备就绪
- 使用 Dart SDK 的 2.19.6 版本。
- 使用
dart pub outdated --mode=null-safety
确保所有依赖项都支持 null safety 并已更新到最新版本。
通过在包含包的 pubspec.yaml
文件的目录中运行 dart migrate
命令来启动迁移工具
dart migrate
如果你的包已准备好迁移,工具会生成如下所示的一行
View the migration suggestions by visiting:
http://127.0.0.1:60278/Users/you/project/mypkg.console-simple?authToken=Xfz0jvpyeMI%3D
在 Chrome 浏览器中访问该 URL,查看交互式 UI,你可以在其中指导迁移过程
对于每个变量和类型注解,你可以看到工具推断的可空性。例如,在上面的屏幕截图中,工具推断第 1 行的 ints
列表(以前是 int
列表)是可空的,因此应该是一个 int?
列表。
理解迁移结果
#要查看每个更改(或非更改)的原因,请在Proposed Edits面板中单击其行号。原因会显示在Edit Details面板中。
例如,考虑以下代码,这是在 null safety 之前编写的
var ints = const <int>[0, null];
var zero = ints[0];
var one = zero + 1;
var zeroOne = <int>[zero, one];
当此代码位于函数外部时(在函数内部不同),默认的迁移方式向后兼容,但并非理想
var ints = const <int?>[0, null];
var zero = ints[0];
var one = zero! + 1;
var zeroOne = <int?>[zero, one];
通过单击第 3 行链接,你可以看到迁移工具添加 !
的原因。因为你知道 zero
不可能为 null,所以你可以改进迁移结果。
改进迁移结果
#当分析推断出错误的可空性时,你可以通过插入临时提示标记来覆盖其建议的更改
在迁移工具的Edit Details面板中,你可以使用Add
/*?*/
hint和Add/*!*/
hint按钮插入提示标记。这些按钮会立即将注释添加到你的文件中,并且无法撤消。如果你不想要工具插入的提示,可以使用常用的代码编辑器将其删除。
你可以使用编辑器添加提示标记,即使工具仍在运行。由于你的代码尚未选择加入 null safety,因此无法使用新的 null safety 特性。但是,你可以进行不依赖于 null safety 特性的更改,例如重构。
完成代码编辑后,单击Rerun from sources以加载你的更改。
下表显示了你可以用来更改迁移工具建议的编辑的提示标记。
提示标记 | 对迁移工具的影响 |
---|---|
表达式 /!/ | 在迁移后的代码中添加 ! ,将表达式强制转换为其底层不可空类型。 |
类型 /!/ | 将类型标记为不可空。 |
/*?*/ | 将前面的类型标记为可空。 |
/*late*/ | 将变量声明标记为 late ,表示它具有延迟初始化。 |
/*late final*/ | 将变量声明标记为 late final ,表示它具有延迟的、一次性初始化。 |
/*required*/ | 将参数标记为 required 。 |
一个提示可能会在代码的其他地方产生连锁反应。在前面的示例中,手动在为 zero
赋值的位置(第 2 行)添加 /*!*/
标记,会使迁移工具将 zero
的类型推断为 int
,而不是 int?
。这种类型更改可能会影响直接或间接使用 zero
的代码。
var zero = ints[0]/*!*/;
有了上述提示,迁移工具会改变其建议的编辑,如下面的代码片段所示。第 3 行 zero
后面不再有 !
,第 4 行 zeroOne
被推断为 int
列表,而不是 int?
列表。
首次迁移 | 带有提示的迁移 |
---|---|
dart
| dart
|
退出文件
#虽然我们建议一次性迁移所有代码,但有时这并不实际,尤其是在大型应用或包中。要退出一个文件或目录,请单击其绿色的复选框。稍后,当你应用更改时,每个退出文件除了 2.9 版本注释外,将保持不变。
有关增量迁移的更多信息,请参阅 不健全的 null safety。
请注意,只有完全迁移的应用和包才与 Dart 3 兼容。
应用更改
#当你对迁移工具建议的所有更改都满意时,点击Apply migration。迁移工具会删除提示标记并保存迁移后的代码。工具还会更新 pubspec 文件中的最低 SDK 约束,这会将包选择加入 null safety。
下一步是静态分析你的代码。如果代码有效,然后测试你的代码。然后,如果你已在 pub.dev 上发布了代码,则发布一个支持 null safety 的预发布版本。
手动迁移
#如果你不想使用迁移工具,可以手动迁移。
我们建议你首先迁移叶子库——不导入包中其他文件的库。然后迁移直接依赖于叶子库的库。最后迁移具有最多包内依赖项的库。
例如,假设你有一个 lib/src/util.dart
文件,它导入了其他(支持 null safety 的)包和核心库,但没有任何 import '<local_path>'
指令。考虑首先迁移 util.dart
,然后迁移仅依赖于 util.dart
的简单文件。如果任何库有循环导入(例如,A 导入 B,B 导入 C,而 C 导入 A),考虑将这些库一起迁移。
要手动迁移包,请按照以下步骤操作
编辑包的
pubspec.yaml
文件,将最低 SDK 约束设置为至少2.12.0
yamlenvironment: sdk: '>=2.12.0 <3.0.0'
重新生成包配置文件
dart pub get
运行
dart pub get
并将最低 SDK 约束设置为至少2.12.0
会将包中每个库的默认语言版本设置为最低 2.12,从而使它们全部选择加入 null safety。在你的 IDE 中打开包。
你可能会看到许多分析错误。没关系。使用分析器识别静态错误,迁移每个 Dart 文件的代码。
通过根据需要添加?
、!
、required
和late
来消除静态错误。
有关手动迁移代码的更多帮助,请参阅 不健全的 null safety。
3. 分析
#更新你的包(在 IDE 或命令行中使用 dart pub get
)。然后使用你的 IDE 或命令行对你的代码执行静态分析
dart pub get
dart analyze # or `flutter analyze`
4. 测试
#如果你的代码通过了分析,则运行测试
dart test # or `flutter test`
你可能需要更新期望 null 值的测试。
如果你需要对代码进行较大更改,可能需要重新迁移。如果是这样,请在使用迁移工具之前恢复你的代码更改。
5. 发布
#我们鼓励你在迁移完成后尽快发布包——可能作为预发布版本
- 设置包版本以指示重大变更。
- 更新 SDK 约束和包依赖项。
- 发布包。如果你不认为此版本是稳定版本,则将包发布为预发布版本。
- 更新示例和文档
更新包版本
#更新包版本以指示重大变更
如果你的包版本已达到
1.0.0
或更高,则增加主版本号。例如,如果先前版本是2.3.2
,新版本则是3.0.0
。如果你的包尚未达到
1.0.0
,则可以增加次版本号或者将版本更新为1.0.0
。例如,如果先前版本是0.3.2
,新版本可以是0.4.0
或1.0.0
。
检查你的 pubspec 文件
#在发布包的稳定 null safety 版本之前,我们强烈建议遵循这些 pubspec 规则
- 将 Dart 的最低 SDK 约束设置为你已测试过的最低稳定版本(至少
2.12.0
)。 - 使用所有直接依赖项的稳定版本。
更新示例和文档
#如果你尚未这样做,请更新包的所有示例和样本,使其使用已迁移版本的包并选择加入 null safety。
如果你为你的包发布了任何单独的文档或教程,也请确保它们已更新到支持 null safety 的版本。
null safety 欢迎你
#如果你已经完成了这些步骤,你应该拥有一个完全迁移、支持 null safety 的 Dart 包。
如果你依赖的所有包也已迁移,那么你的程序在 null 引用错误方面是健全的。在运行或编译代码时,你应该会看到类似如下的输出
Compiling with sound null safety
Dart 团队全体成员衷心感谢你迁移你的代码。