本文将介绍如何将你的代码迁移至 空安全。以下是对你的 package 逐个迁移的基本步骤:

This page describes how and when to migrate your code to null safety. Here are the basic steps for migrating each package that you own:

  1. 等待 你依赖的 package 迁移完成。

    Wait for the packages that you depend on to migrate.

  2. 迁移 你的 package 的代码,最好使用交互式的迁移工具。

    Migrate your package’s code, preferably using the interactive migration tool.

  3. 静态分析 package 的代码。

    Statically analyze your package’s code.

  4. 测试 你的代码,确保可用。

    Test to make sure your changes work.

  5. 如果你已经在 pub.flutter-io.cn 发布了你的 package,可以将迁移完成的空安全版本以 预发布 版本进行 发布

    If the package is already on pub.dev, publish the null-safe version as a prerelease version.


For an informal look at the experience of using the migration tool, watch this video:


1. 等待迁移

1. Wait to migrate

我们强烈建议你按顺序迁移代码,先迁移依赖关系中的处于最末端的依赖。例如,如果 C 依赖了 B,B 依赖了 A,那么应该按照 A -> B -> C 的顺序进行迁移。

We strongly recommend migrating code in order, with the leaves of the dependency graph being migrated first. For example, if package C depends on package B, which depends on package A, then A should be migrated to null safety first, then B, then C.

Illustration of C/B/A sentence

虽然你在你的所有依赖迁移完成前就 可以 进行迁移,但在它们迁移完成后,你可能需要再对你的代码进行调整。例如,如果你推测一个函数可以接受一个可空的参数,但依赖的 package 迁移后变为了非空,在传递可空的参数时便会出现编译错误。

Although you can migrate before your dependencies support null safety, you might have to change your code when your dependencies migrate. For example, if you predict that a function will take a nullable parameter but the package migrates it to be non-nullable, then passing a nullable argument becomes a compile error.

该节会讲述如何在空安全模式下,使用 dart pub outdated 检查并更新你的依赖。如果你的代码应用了 版本管理,你可以随时回滚所有的改动。

This section tells you how to check and update your package’s dependencies, with the help of the dart pub outdated command in null-safety mode. The instructions assume your code is under source control, so that you can easily undo any changes.

切换至 2.12 beta 版本

Switch to the latest stable Dart release

切换至最新的稳定版本 的 Dart SDK 或 Flutter SDK。你可以根据你的使用情况,参考以下的方式获取 SDK:

Switch to the latest stable release of either the Dart SDK or the Flutter SDK.

查看你的 Dart 版本是否为 2.12 或更高:

Check that you have Dart 2.12 or later:

$ dart --version


Check dependency status

通过以下命令检查你的 package 的迁移状态:

Get the migration state of your package’s dependencies, using the following command:

$ dart pub outdated --mode=null-safety

如果你看到所有依赖都已支持空安全,就意味着你可以开始迁移了。否则请使用 Resolvable 列内列举的已迁移至空安全的版本。

If the output says that all the packages support null safety, then you can start migrating. Otherwise, use the Resolvable column to find null-safe releases, if they exist.

这是一个简单的 package 的输入示例。每个 package 的绿色对勾代表着对应版本已支持空安全:

Here’s an example of the output for a simple package. The green checkmarked version for each package supports null safety:

Output of dart pub outdated

上面的输出说明了所有依赖的 package 都有可使用的已支持空安全的预发布版本。

The output shows that all of the package’s dependencies have resolvable prereleases that support null safety.

如果你的 package 的依赖中,有一些 尚未 支持空安全,我们推荐你联系对应依赖的作者。你可以在 pub.flutter-io.cn 对应 package 的页面,找到作者的联系信息。

If any of your package’s dependencies don’t yet support null safety, we encourage you to reach out to the package owner. You can find contact details on the package page on pub.dev.


Update dependencies

在迁移你的 package 的代码之前,请将它的依赖项升级至空安全版本。

Before migrating your package’s code, update its dependencies to null-safe versions:

  1. 运行 dart pub upgrade --null-safety 将依赖升级至支持空安全的最新版本。 注意: 该命令会更改你的 pubspec.yaml 文件。

    Run dart pub upgrade --null-safety to upgrade to the latest versions supporting null safety. Note: This command changes your pubspec.yaml file.

  2. 运行 dart pub upgrade

    Run dart pub get.

2. 迁移

2. Migrate

你的代码里大部分需要更改的代码,都是可以轻易推导的。例如,如果一个变量可以为空,它的类型需要 ? 后缀。一个不可以为空的命名参数,需要使用 required 标记

Most of the changes that your code needs to be null safe are easily predictable. For example, if a variable can be null, its type needs a ? suffix. A named parameter that shouldn’t be nullable needs to be marked required.


You have two options for migrating:


Using the migration tool

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

The migration tool takes a package of null-unsafe Dart code and converts it to null safety. You can guide the tool’s conversion by adding hint markers to your Dart code.


Before starting the tool, make sure you’re ready:

  • 使用最新的 Dart SDK 稳定版本。

    Use the latest beta release of the Dart SDK.

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

    Use dart pub outdated --mode=null-safety to make sure that all dependencies are null safe and up-to-date.

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

Start the migration tool by running the dart migrate command in the directory that contains the package’s pubspec.yaml file:

$ dart migrate

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

If your package is ready to migrate, then the tool produces a line like the following:

View the migration suggestions by visiting:

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

Visit that URL in a Chrome browser to see an interactive UI where you can guide the migration process:

Screenshot of migration tool

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

For every variable and type annotation, you can see what nullability the tool infers. For example, in the preceding screenshot, the tool infers that the ints list (previously a list of int) in line 1 is nullable, and thus should be a list of int?.


Understanding migration results

若要了解每个变化(或者未变化)的原因,点击 Proposed Edits 窗口中的行数,原因会出现在 Edit Details 窗口中。

To see the reasons for each change (or non-change), click its line number in the Proposed Edits pane. The reasons appear in the Edit Details pane.


For example, consider the following code, from before null safety:

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


The default migration when this code is outside a function (it’s different within a function) is backward compatible but not ideal:

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

点击 line 3 链接,你可以看到迁移工具添加 ! 的原因。而因为你知道 zero 不会为空,所以你可以改进迁移结果。

By clicking the line 3 link, you can see the migration tool’s reasons for adding the !. Because you know that zero can’t be null, you can improve the migration result.


Improving migration results


When analysis infers the wrong nullability, you can override its proposed edits by inserting temporary hint markers:

  • 在迁移工具的 Edit Details 窗格中,你可以通过 Add /*?*/ hintAdd /*!*/ hint 按钮来添加提示标记。

    In the Edit Details pane of the migration tool, you can insert hint markers using the Add /*?*/ hint and Add /*!*/ hint buttons.

    按下这些按钮,相应的标记会立刻添加到代码中,并且 无法撤销。如果你想删除标记,可以和平常一样使用代码编辑器删除它。

    These buttons add comments to your file immediately, and there’s no Undo. If you don’t want a hint that the tool inserted, you can use your usual code editor to remove it.

  • 就算迁移工具正在运行,你也可以使用编辑器添加提示标记。由于你的代码还未迁移到空安全,所以无法使用空安全的新特性。但是你可以进行与空安全无关的改动,例如重构。

    You can use an editor to add hint markers, even while the tool is still running. Because your code hasn’t opted into null safety yet, you can’t use new null-safety features. You can, however, make changes like refactoring that don’t depend on null-safety features.

    当你完成编辑后,点击 Rerun from sources 进行更改。

    When you’ve finished editing your code, click Rerun from sources to pick up your changes.


The following table shows the hint markers that you can use to change the migration tool’s proposed edits.

提示标记Hint marker 对迁移工具的影响Effect on the migration tool
expression /*!*/ 添加 `!` 至代码中,将 **表达式** 转换为其基础类型对应的不可空的类型。Adds a `!` to the migrated code, casting _expression_ to its underlying non-nullable type.
type /*!*/ 将 **类型** 标记为非空。Marks _type_ as non-nullable.
/*?*/ 将前面的类型标记为可空。Marks the preceding type as nullable.
/*late*/ 将变量声明标记为 `late`,表示其不会第一时间进行初始化。Marks the variable declaration as `late`, indicating that it has late initialization.
/*late final*/ 将变量声明标记为 `late final`,表示其不会第一时间进行初始化,且初始化后不可改变。Marks the variable declaration as `late final`, indicating that it has late, one-time initialization.
/*required*/ 将参数标记为 `required`。Marks the parameter as `required`.

一个提示也可能产生蝴蝶效应,影响其他的代码。在先前的例子中,如果在 zero 被赋值的位置(第二行)添加一个 /*!*/ 标记,迁移工具就会推断 zero 的类型是 int 而非 int?。这就会影响到直接或间接使用了 zero 的代码。

A single hint can have ripple effects elsewhere in the code. In the example from before, manually adding a /*!*/ marker where zero is assigned its value (on line 2) makes the migration tool infer the type of zero as int instead of int?. This type change can affect code that directly or indirectly uses zero.

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

通过添加了以上的提示,迁移工具将调整建议的更改,如下面的代码所示。第三行的 zero 后面不再有 !,第四行的 zeroOne 也被推断为 int 列表而不是 int?

With the above hint, the migration tool changes its proposed edits, as the following code snippets show. Line 3 no longer has a ! after zero, and in line 4 zeroOne is inferred to be a list of int, not int?.

首次迁移First migration 添加提示后的迁移Migration with hint
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];


Opting out files

尽管我们希望你能一次性完成迁移工作,但对于大体量的应用或 package 而言并不是简单的事。如果你想只迁移部分文件,请将暂时不迁移的文件前方的绿色勾选框取消勾选。稍后应用迁移更改时,这些文件会加上 Dart 2.9 版本注释,其他内容保持不变。

Although we recommend migrating all at once, sometimes that isn’t practical, especially in a large app or package. To opt out a file or directory, click its green checkbox. Later, when you apply changes, each opted out file will be unchanged except for a 2.9 version comment.

更多有关渐进迁移空安全的内容,请阅读 非健全的空安全

For more information about incremental migration, see Unsound null safety.


Applying changes

当你觉得迁移工具提示的更改部分可以应用了,点击 Apply migration。迁移工具会删除所有的提示标记,保存迁移后的代码。同时,迁移工具也会更改 pubspec 的 SDK 限制,将 package 迁移至空安全。

When you like all of the changes that the migration tool proposes, click Apply migration. The migration tool deletes the hint markers and saves the migrated code. The tool also updates the minimum SDK constraint in the pubspec, which opts the package into null safety.

下一步就是对代码进行 静态分析。如果一切正常,下一步就是 测试你的代码。最后,如果你已经将 package 发布至 pub.flutter-io.cn, 发布空安全的预览版本

The next step is to statically analyze your code. If it’s valid, then test your code. Then, if you’ve published your code on pub.dev, publish a null-safe prerelease.


Migrating by hand


If you prefer not to use the migration tool, you can migrate manually.

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

We recommend that you first migrate leaf libraries — libraries that don’t import other files from the package. Then migrate libraries that directly depend on the leaf libraries. End by migrating the libraries that have the most intra-package dependencies.

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

For example, say you have a lib/src/util.dart file that imports other (null-safe) packages and core libraries, but that doesn’t have any import '<local_path>' directives. Consider migrating util.dart first, and then migrating simple files that depend only on util.dart. If any libraries have cyclic imports (for example, A imports B which imports C, and C imports A), consider migrating those libraries together.

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

To migrate a package by hand, follow these steps:

  1. 编辑 package 的 pubspec.yaml 文件,将最低 SDK 版本设置到至少为 2.12.0

    Edit the package’s pubspec.yaml file, setting the minimum SDK constraint to at least 2.12.0:

      sdk: '>=2.12.0 <3.0.0'
  2. 重新生成 package 的配置文件

    Regenerate the package configuration file:

    $ dart pub get

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

    Running dart pub get with a lower SDK constraint of at least 2.12.0 sets the default language version of every library in the package to a minimum of 2.12, opting them all in to null safety.

  3. 在你的 IDE 上打开package 。

    Open the package in your IDE.
    You’re likely to see a lot of analysis errors. That’s OK.

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

    Migrate the code of each Dart file, using the analyzer to identify static errors.
    Eliminate static errors by adding ?, !, required, and late, as needed.

想获得更多手动迁移的帮助,请前往 非健全的空安全

See Unsound null safety for more help on migrating code by hand.

3. 分析

3. Analyze

更新你的 package(在 IDE 或命令行工具中使用 dart pub get)后在 IDE 或命令行工具中对你的代码进行 静态分析

Update your packages (using dart pub get in your IDE or on the command line). Then use your IDE or the command line to perform static analysis on your code:

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

4. 测试

4. Test


If your code passes analysis, run tests:

$ dart test       # or `flutter test`


You might need to update tests that expect null values.


If you need to make large changes to your code, then you might need to remigrate it. If so, revert your code changes before using the migration tool again.

5. 发布

5. Publish


We encourage you to publish packages — possibly as prereleases — as soon as you migrate:

Set the lower SDK constraint to 2.12.0:

  sdk: '>=2.12.0 <3.0.0'

With these constraints, packages that are published during null safety beta can still work with the next stable release of the Dart SDK.

更新 package 的版本

Package 的版本

Update the package version


Update the version of the package to indicate a breaking change:

  • 如果你的 package 版本已经大于或等于 1.0.0,请提升主版本。例如,上一个版本为 2.3.2,那么新版本应该为 3.0.0

    If your package is already at 1.0.0 or greater, increase the major version. For example, if the previous version is 2.3.2, the new version is 3.0.0.

  • 如果你的 package 的版本还未高于 1.0.0,你可以 提升次版本,也可以 提升至 1.0.0。例如,上一个版本为 0.3.2,那么新版本可以是

    If your package hasn’t reached 1.0.0 yet, either increase the minor version or update the version to 1.0.0. For example, if the previous version is 0.3.2, the new version is either 0.4.0 or 1.0.0.

检查你的 pubspec

Check your pubspec

在你发布稳定版本的空安全 package 前,我们强烈建议你遵循以下 pubspec 的规则:

Before you publish a stable null safety version of a package, we strongly recommend following these pubspec rules:

  • 将 SDK 的最低限制设置为你测试过的最低稳定版本(至少是 2.12.0)。

    Set the Dart lower SDK constraint to the lowest stable version that you’ve tested against (at least 2.12.0).

  • 所有的直接依赖都使用稳定版本。

    Use stable versions of all direct dependencies.


Welcome to null safety

如果你走到了这一步,你应该已经完全将你的 Dart package 迁移至空安全了。当你的所有依赖也都完成了迁移,那么你的程序就是健全的,同时可以正确处理空引用的错误。

If you made it this far, you should have a fully migrated, null-safe Dart package. If all of the packages you depend on are migrated too, then your program is sound with respect to null-reference errors.

谨代表 Dart 团队的所有成员,感谢你 迁移你的代码。

From all of the Dart team, thank you for migrating your code.