跳到主要內容

遷移到 null safety

本頁介紹如何以及何時將程式碼遷移到 null safety。以下是遷移你擁有的每個包的基本步驟

  1. 等待你依賴的包完成遷移。
  2. 遷移你的包程式碼,最好使用互動式遷移工具。
  3. 靜態分析你的包程式碼。
  4. 測試以確保你的更改有效。
  5. 如果包已在 pub.dev 上,則以預釋出 (prerelease) 版本釋出支援 null safety 的版本。

要非正式地瞭解使用遷移工具的體驗,請觀看此影片

在 YouTube 新標籤頁中觀看:“如何將 Dart 包遷移到 null safety”

1. 等待遷移

#

我們強烈建議按順序遷移程式碼,首先遷移依賴關係圖的葉子節點。例如,如果包 C 依賴於包 B,而包 B 依賴於包 A,那麼應首先將 A 遷移到 null safety,然後是 B,最後是 C。

Illustration of C/B/A sentence

儘管你可以在依賴項支援 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

Output of dart pub outdated

輸出顯示,該包的所有依賴項都有可解析的、支援 null safety 的預釋出版本。

如果你的包的任何依賴項尚未支援 null safety,我們鼓勵你聯絡包所有者。你可以在 pub.dev 上的包頁面上找到聯絡方式。

更新依賴項

#

在遷移你的包程式碼之前,將其依賴項更新到支援 null safety 的版本

  1. 執行 dart pub upgrade --null-safety 將依賴項升級到支援 null safety 的最新版本。注意:此命令會更改你的 pubspec.yaml 檔案。

  2. 執行 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,你可以在其中指導遷移過程

Screenshot of migration tool

對於每個變數和型別註解,你可以看到工具推斷的可空性。例如,在上面的螢幕截圖中,工具推斷第 1 行的 ints 列表(以前是 int 列表)是可空的,因此應該是一個 int? 列表。

理解遷移結果

#

要檢視每個更改(或非更改)的原因,請在Proposed Edits面板中單擊其行號。原因會顯示在Edit Details面板中。

例如,考慮以下程式碼,這是在 null safety 之前編寫的

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 不可能為 null,所以你可以改進遷移結果。

改進遷移結果

#

當分析推斷出錯誤的可空性時,你可以透過插入臨時提示標記來覆蓋其建議的更改

  • 在遷移工具的Edit Details面板中,你可以使用Add /*?*/ hintAdd /*!*/ hint按鈕插入提示標記。

    這些按鈕會立即將註釋新增到你的檔案中,並且無法撤消。如果你不想要工具插入的提示,可以使用常用的程式碼編輯器將其刪除。

  • 你可以使用編輯器新增提示標記,即使工具仍在執行。由於你的程式碼尚未選擇加入 null safety,因此無法使用新的 null safety 特性。但是,你可以進行不依賴於 null safety 特性的更改,例如重構。

    完成程式碼編輯後,單擊Rerun from sources以載入你的更改。

下表顯示了你可以用來更改遷移工具建議的編輯的提示標記。

提示標記對遷移工具的影響
表示式 /!/在遷移後的程式碼中新增 !,將表示式強制轉換為其底層不可空型別。
型別 /!/型別標記為不可空。
/*?*/將前面的型別標記為可空。
/*late*/將變數宣告標記為 late,表示它具有延遲初始化。
/*late final*/將變數宣告標記為 late final,表示它具有延遲的、一次性初始化。
/*required*/將引數標記為 required

一個提示可能會在程式碼的其他地方產生連鎖反應。在前面的示例中,手動在為 zero 賦值的位置(第 2 行)新增 /*!*/ 標記,會使遷移工具將 zero 的型別推斷為 int,而不是 int?。這種型別更改可能會影響直接或間接使用 zero 的程式碼。

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

有了上述提示,遷移工具會改變其建議的編輯,如下面的程式碼片段所示。第 3 行 zero 後面不再有 !,第 4 行 zeroOne 被推斷為 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];

退出檔案

#

雖然我們建議一次性遷移所有程式碼,但有時這並不實際,尤其是在大型應用或包中。要退出一個檔案或目錄,請單擊其綠色的複選框。稍後,當你應用更改時,每個退出檔案除了 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),考慮將這些庫一起遷移。

要手動遷移包,請按照以下步驟操作

  1. 編輯包的 pubspec.yaml 檔案,將最低 SDK 約束設定為至少 2.12.0

    yaml
    environment:
      sdk: '>=2.12.0 <3.0.0'
  2. 重新生成包配置檔案

    dart pub get

    執行 dart pub get 並將最低 SDK 約束設定為至少 2.12.0 會將包中每個庫的預設語言版本設定為最低 2.12,從而使它們全部選擇加入 null safety。

  3. 在你的 IDE 中開啟包。
    你可能會看到許多分析錯誤。沒關係。

  4. 使用分析器識別靜態錯誤,遷移每個 Dart 檔案的程式碼。
    透過根據需要新增 ?!requiredlate 來消除靜態錯誤。

有關手動遷移程式碼的更多幫助,請參閱 不健全的 null safety

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 檔案

#

在釋出包的穩定 null safety 版本之前,我們強烈建議遵循這些 pubspec 規則

  • 將 Dart 的最低 SDK 約束設定為你已測試過的最低穩定版本(至少 2.12.0)。
  • 使用所有直接依賴項的穩定版本。

更新示例和文件

#

如果你尚未這樣做,請更新包的所有示例和樣本,使其使用已遷移版本的包並選擇加入 null safety。

如果你為你的包釋出了任何單獨的文件或教程,也請確保它們已更新到支援 null safety 的版本。

null safety 歡迎你

#

如果你已經完成了這些步驟,你應該擁有一個完全遷移、支援 null safety 的 Dart 包。

如果你依賴的所有包也已遷移,那麼你的程式在 null 引用錯誤方面是健全的。在執行或編譯程式碼時,你應該會看到類似如下的輸出

Compiling with sound null safety

Dart 團隊全體成員衷心感謝你遷移你的程式碼。