目录

迁移到空安全

此页面介绍如何以及何时将您的代码迁移到空安全。以下是迁移您拥有的每个包的基本步骤

  1. 等待您依赖的包迁移。
  2. 迁移您包的代码,最好使用交互式迁移工具。
  3. 静态分析您包的代码。
  4. 测试以确保您的更改有效。
  5. 如果该包已在 pub.dev 上,请将空安全版本发布预发布版本。

要非正式地了解使用迁移工具的体验,请观看此视频


如何将 Dart 包迁移到空安全

1. 等待迁移

#

我们强烈建议按顺序迁移代码,先迁移依赖关系图的叶节点。例如,如果包 C 依赖于包 B,而包 B 又依赖于包 A,则应首先将 A 迁移到空安全,然后是 B,最后是 C。

Illustration of C/B/A sentence

尽管您可以您的依赖项支持空安全之前进行迁移,但在您的依赖项迁移时,您可能必须更改您的代码。例如,如果您预测一个函数将接受一个可为空的参数,但该包将其迁移为不可为空,那么传递可为空的参数将成为编译错误。

本节将告诉您如何在空安全模式下,借助 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

如果输出表明所有包都支持空安全,那么您可以开始迁移。否则,请使用可解析列查找空安全版本(如果存在)。

以下是简单包的输出示例。每个包的绿色勾选版本都支持空安全

Output of dart pub outdated

输出显示,该包的所有依赖项都有支持空安全的可解析预发布版本。

如果您的包的任何依赖项尚未支持空安全,我们建议您联系包的所有者。您可以在 pub.dev 上的包页面上找到联系方式。

更新依赖项

#

在迁移您包的代码之前,请将其依赖项更新到空安全版本

  1. 运行 dart pub upgrade --null-safety 以升级到支持空安全的最新版本。注意:此命令会更改您的 pubspec.yaml 文件。

  2. 运行 dart pub get

2. 迁移

#

您的代码需要空安全的大部分更改都是可以轻松预测的。例如,如果变量可以是 null,则其类型需要一个 ? 后缀。如果命名参数不应为空,请将其标记为required或为其提供默认值

您有两个迁移选项

使用迁移工具

#

迁移工具接受一个不安全的 Dart 代码包,并将其转换为空安全。您可以通过将提示标记添加到您的 Dart 代码中来指导工具的转换。

在启动该工具之前,请确保您已准备就绪

  • 使用 Dart SDK 的 2.19.6 版本。
  • 使用 dart pub outdated --mode=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,您可以在其中指导迁移过程

Screenshot of migration tool

对于每个变量和类型注释,您可以查看工具推断的 nullability。例如,在前面的屏幕截图中,工具推断第 1 行中的 ints 列表(以前是 int 列表)是可空的,因此应该是 int? 的列表。

了解迁移结果

#

要查看每个更改(或未更改)的原因,请单击建议的编辑窗格中的行号。原因会显示在编辑详情窗格中。

例如,考虑以下在空安全之前的代码:

dart
var ints = const <int>[0, null];
var zero = ints[0];
var one = zero + 1;
var zeroOne = <int>[zero, one];

当此代码位于函数之外时(在函数内部有所不同),默认迁移是向后兼容的,但并非理想的。

dart
var ints = const <int?>[0, null];
var zero = ints[0];
var one = zero! + 1;
var zeroOne = <int?>[zero, one];

通过单击第 3 行链接,您可以查看迁移工具添加 ! 的原因。因为您知道 zero 不可能为空,所以您可以改进迁移结果。

改进迁移结果

#

当分析推断出错误的 nullability 时,您可以通过插入临时提示标记来覆盖其建议的编辑。

  • 在迁移工具的编辑详情窗格中,您可以使用添加 /*?*/ 提示添加 /*!*/ 提示按钮插入提示标记。

    这些按钮会立即将注释添加到您的文件中,并且没有撤消。如果您不想要工具插入的提示,可以使用常用的代码编辑器将其删除。

  • 您可以使用编辑器添加提示标记,即使工具仍在运行。由于您的代码尚未选择加入空安全,因此您不能使用新的空安全功能。但是,您可以进行不依赖于空安全功能的操作,例如重构。

    完成代码编辑后,单击从源代码重新运行以获取您的更改。

下表显示了可用于更改迁移工具建议的编辑的提示标记。

提示标记对迁移工具的影响
expression /!/! 添加到迁移后的代码中,将 *expression* 强制转换为其基础的不可空类型。
type /!/将 *type* 标记为不可空。
/*?*/将前面的类型标记为可空。
/*late*/将变量声明标记为 late,表示它具有延迟初始化。
/*late final*/将变量声明标记为 late final,表示它具有延迟的、一次性初始化。
/*required*/将参数标记为 required

单个提示可能会在代码的其他位置产生连锁反应。在之前的示例中,手动在 zero 被赋值的位置(第 2 行)添加 /*!*/ 标记,会使迁移工具推断出 zero 的类型为 int 而不是 int?。此类型更改可能会影响直接或间接使用 zero 的代码。

dart
var zero = ints[0]/*!*/;

通过上述提示,迁移工具会更改其建议的编辑,如下面的代码片段所示。第 3 行在 zero 之后不再有 !,并且在第 4 行中,推断出 zeroOneint 的列表,而不是 int?

首次迁移带有提示的迁移
dart
var ints = const <int?>[0, null];
var zero = ints[0];
var one = zero! + 1;
var zeroOne = <int?>[zero, one];
dart
var ints = const <int?>[0, null];
var zero = ints[0]/*!*/;
var one = zero + 1;
var zeroOne = <int>[zero, one];

选择退出文件

#

虽然我们建议一次性迁移所有内容,但有时这并不实际,尤其是在大型应用程序或软件包中。要选择退出文件或目录,请单击其绿色复选框。稍后,当您应用更改时,每个选择退出的文件都将保持不变,除了 2.9 版本注释

有关增量迁移的更多信息,请参阅不健全的空安全

请注意,只有完全迁移的应用程序和软件包才与 Dart 3 兼容。

应用更改

#

当您喜欢迁移工具提出的所有更改时,单击应用迁移。迁移工具会删除提示标记并保存迁移后的代码。该工具还会更新 pubspec 中的最低 SDK 约束,从而使软件包选择加入空安全。

下一步是静态分析您的代码。如果它有效,则测试您的代码。然后,如果您已在 pub.dev 上发布了您的代码,则发布空安全的预发布版本

手动迁移

#

如果您不想使用迁移工具,则可以手动迁移。

我们建议您首先迁移叶库 - 不从软件包导入其他文件的库。然后迁移直接依赖于叶库的库。最后迁移具有最多软件包内依赖关系的库。

例如,假设您有一个 lib/src/util.dart 文件,该文件导入其他(空安全)软件包和核心库,但不包含任何 import '<local_path>' 指令。考虑先迁移 util.dart,然后迁移仅依赖于 util.dart 的简单文件。如果任何库具有循环导入(例如,A 导入 B,B 导入 C,而 C 导入 A),请考虑将这些库一起迁移。

要手动迁移软件包,请按照以下步骤操作

  1. 编辑软件包的 pubspec.yaml 文件,将最低 SDK 约束设置为至少 2.12.0

    yaml
    environment:
      sdk: '>=2.12.0 <3.0.0'
  2. 重新生成软件包配置文件

    $ dart pub get

    使用至少 2.12.0 的较低 SDK 约束运行 dart pub get 会将软件包中每个库的默认语言版本设置为至少 2.12,从而使它们全部选择加入空安全。

  3. 在 IDE 中打开软件包。
    您可能会看到很多分析错误。没关系。

  4. 使用分析器识别静态错误,迁移每个 Dart 文件的代码。
    根据需要添加 ?!requiredlate,以消除静态错误。

有关手动迁移代码的更多帮助,请参阅不健全的空安全

3. 分析

#

更新您的软件包(在 IDE 中或命令行中使用 dart pub get)。然后使用 IDE 或命令行对您的代码执行静态分析

$ dart pub get
$ dart analyze     # or `flutter analyze`

4. 测试

#

如果您的代码通过了分析,请运行测试

$ dart test       # or `flutter test`

您可能需要更新期望 null 值的测试。

如果需要对代码进行较大更改,则可能需要重新迁移它。如果是这样,请在再次使用迁移工具之前还原您的代码更改。

5. 发布

#

我们鼓励您尽快发布软件包(可能以预发布版本形式发布),以便您进行迁移

更新包版本

#

更新软件包的版本以指示重大更改

  • 如果您的软件包已达到 1.0.0 或更高版本,请增加主要版本。例如,如果之前的版本是 2.3.2,则新版本是 3.0.0

  • 如果您的软件包尚未达到 1.0.0 增加次要版本将版本更新为 1.0.0。例如,如果之前的版本是 0.3.2,则新版本是 0.4.01.0.0

检查您的 pubspec

#

在发布软件包的稳定空安全版本之前,我们强烈建议遵循以下 pubspec 规则

  • 将 Dart 的最低 SDK 约束设置为您测试过的最低稳定版本(至少 2.12.0)。
  • 使用所有直接依赖项的稳定版本。

欢迎使用空安全

#

如果您能做到这一步,您应该拥有一个完全迁移的、空安全的 Dart 软件包。

如果您依赖的所有软件包也已迁移,那么您的程序在空引用错误方面是健全的。当运行或编译您的代码时,您应该看到如下所示的输出

Compiling with sound null safety

来自 Dart 团队的所有成员,感谢您迁移您的代码。