目錄

遷移至空安全

本文將介紹如何將你的程式碼遷移至 空安全。以下是對你的 package 逐個遷移的基本步驟:

  1. 等待 你依賴的 package 遷移完成。

  2. 遷移 你的 package 的程式碼,最好使用互動式的遷移工具。

  3. 靜態分析 package 的程式碼。

  4. 測試 你的程式碼,確保可用。

  5. 如果你已經在 pub.dev 上釋出了你的 package,可以將遷移完成的空安全版本以 預釋出 版本進行 釋出

如果你想預覽遷移工具的體驗,可以檢視以下影片:

可互動的遷移工具讓你可以簡化遷移至空安全的過程。

1. 等待遷移

我們強烈建議你按順序遷移程式碼,先遷移依賴關係中的處於最末端的依賴。例如,如果 C 依賴了 B,B 依賴了 A,那麼應該按照 A -> B -> C 的順序進行遷移。

Illustration of C/B/A sentence

雖然你在你的所有依賴遷移完成前就 可以 進行遷移,但在它們遷移完成後,你可能需要再對你的程式碼進行調整。例如,如果你推測一個函式可以接受一個可空的引數,但依賴的 package 遷移後變為了非空,在傳遞可空的引數時便會出現編譯錯誤。

該節會講述如何在空安全模式下,使用 dart pub outdated 檢查並更新你的依賴。如果你的程式碼應用了 版本管理,你可以隨時回滾所有的改動。

切換至 Dart 2.19 版本

切換到 Dart SDK 的 Dart 2.19 穩定版,它包含在 Flutter 3.7 SDK 中。

執行下面程式碼檢視是否使用了 Dart 2.19 版本:

$ dart --version
Dart SDK version: 2.19.6

檢查所有依賴的遷移狀態

透過以下命令檢查你的 package 的遷移狀態:

$ dart pub outdated --mode=null-safety

如果你看到所有依賴都已支援空安全,就意味著你可以開始遷移了。否則請使用 Resolvable 列內列舉的已遷移至空安全的版本。

這是一個簡單的 package 的輸入範例。每個 package 的綠色對勾代表著對應版本已支援空安全:

Output of dart pub outdated

上面的輸出說明了所有依賴的 package 都有可使用的已支援空安全的預釋出版本。

如果你的 package 的依賴中,有一些 尚未 支援空安全,我們推薦你聯絡對應依賴的作者。你可以在 pub.dev 對應 package 的頁面,找到作者的聯絡資訊。

升級依賴

在遷移你的 package 的程式碼之前,請將它的依賴項升級至空安全版本。

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

  2. 執行命令 dart pub get

2. 遷移

你的程式碼裡大部分需要更改的程式碼,都是可以輕易推導的。例如,如果一個變數可以為空,它的型別需要 ? 字尾。一個不可以為空的命名引數,需要使用 required 標記,或者給定其一個 預設值

針對遷移,你有兩個選項可以選擇:

使用遷移工具

遷移工具會帶上一個非空安全的 package ,將它轉換至空安全。你可以先在程式碼中新增 提示標記 來引導遷移工具的轉換。

開始轉換前,請做好如下的準備:

  • 使用最新的 Dart 2.19 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,你可以看到一個互動式的介面,引導你進行遷移:

Screenshot of migration tool

你可以在工具中看到其推斷的所有變數和型別註解。例如,在上面的截圖中,工具推斷第一行的 ints 列表元素可能為空,所以應該變為 int?(先前為 int)。

理解遷移的結果

若要了解每個變化(或者未變化)的原因,點選 Proposed Edits 視窗中的行數,原因會出現在 Edit Details 視窗中。

舉個例子,假設我們有如下的非空安全的程式碼:

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];

點選 line 3 連結,你可以看到遷移工具新增 ! 的原因。而因為你知道 zero 不會為空,所以你可以改進遷移結果。

改進遷移的結果

當分析結果推導了錯誤的可空性時,你可以新增臨時的提示標記來改變建議的編輯:

  • 在遷移工具的 Edit Details 窗格中,你可以透過 Add /*?*/ hintAdd /*!*/ hint 按鈕來新增提示標記。

    按下這些按鈕,相應的標記會立刻新增到程式碼中,並且 無法撤銷。如果你想刪除標記,可以和平常一樣使用程式碼編輯器刪除它。

  • 就算遷移工具正在執行,你也可以使用編輯器新增提示標記。由於你的程式碼還未遷移到空安全,所以無法使用空安全的新特性。但是你可以進行與空安全無關的改動,例如重構。

    當你完成編輯後,點選 Rerun from sources 進行更改。

下方的表格展示了可以使用的提示標記。

提示標記 對遷移工具的影響
expression /*!*/ 新增 `!` 至程式碼中,將 **表示式** 轉換為其基礎型別對應的不可空的型別。
type /*!*/ 將 **型別** 標記為非空。
/*?*/ 將前面的型別標記為可空。
/*late*/ 將變數宣告標記為 `late`,表示其不會第一時間進行初始化。
/*late final*/ 將變數宣告標記為 `late final`,表示其不會第一時間進行初始化,且初始化後不可改變。
/*required*/ 將引數標記為 `required`。

一個提示也可能產生蝴蝶效應,影響其他的程式碼。在先前的例子中,如果在 zero 被賦值的位置(第二行)新增一個 /*!*/ 標記,遷移工具就會推斷 zero 的型別是 int 而非 int?。這就會影響到直接或間接使用了 zero 的程式碼。

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

透過添加了以上的提示,遷移工具將調整建議的更改,如下面的程式碼所示。第三行的 zero 後面不再有 !,第四行的 zeroOne 也被推斷為 int 列表而不是 int?

首次遷移 新增提示後的遷移
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];

只遷移部分檔案

儘管我們希望你能一次性完成遷移工作,但對於大體量的應用或 package 而言並不是簡單的事。如果你想只遷移部分檔案,請將暫時不遷移的檔案前方的綠色勾選框取消勾選。稍後應用遷移更改時,這些檔案會加上 Dart 2.9 版本註釋,其他內容保持不變。

更多有關漸進遷移空安全的內容,請閱讀 非健全的空安全

請注意,從 Dart 3 開始,只支援完全遷移到空安全的應用和 package。

應用更改

當你覺得遷移工具提示的更改部分可以應用了,點選 Apply migration。遷移工具會刪除所有的提示標記,儲存遷移後的程式碼。同時,遷移工具也會更改 pubspec 的 SDK 限制,將 package 遷移至空安全。

下一步就是對程式碼進行 靜態分析。如果一切正常,下一步就是 測試你的程式碼。最後,如果你已經將 package 釋出至 pub.dev, 釋出空安全的預覽版本

手動遷移

如果你不想使用遷移工具,你也可以手動進行遷移。

我們推薦你 優先遷移最下層的函式庫 —— 指的是沒有匯入其他 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 的 SDK 上執行 dart pub get 時,會將每個 package 的預設 SDK 最低版本設定為 2.12,並且預設它們已經遷移至空安全。

  3. 在你的 IDE 上開啟package 。
    你也許會看到很多錯誤,沒關係,讓我們繼續。

  4. 利用分析器來辨析靜態錯誤,逐個遷移 Dart 檔案。
    按需新增 ?!required 以及 late 來消除靜態錯誤。

想獲得更多手動遷移的幫助,請前往 非健全的空安全

3. 分析

更新你的 package(在 IDE 或命令列工具中使用 dart pub get)後在 IDE 或命令列工具中對你的程式碼進行 靜態分析

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

4. 測試

如果你的程式碼通過了分析,接下來可以開始測試:

$ dart test       # or `flutter test`

你可能需要更新使用了空值作為預期使用案例的測試程式碼。

如果你需要對程式碼作出大量的更改,那麼你可能需要重新對程式碼進行遷移。這時請先回滾程式碼更改,再執行遷移工具進行遷移。

5. 釋出

我們希望你完成遷移後儘快將其釋出,可以作為預覽版:

更新 package 的版本

Package 的版本

你可以修改版本以表示該版本包含了破壞性的改動:

  • 如果你的 package 版本已經大於或等於 1.0.0,請提升主版本。例如,上一個版本為 2.3.2,那麼新版本應該為 3.0.0

  • 如果你的 package 的版本還未高於 1.0.0,你可以 提升次版本,也可以 提升至 1.0.0。例如,上一個版本為 0.3.2,那麼新版本可以是 0.4.01.0.0

檢查你的 pubspec

在你釋出穩定版本的空安全 package 前,我們強烈建議你遵循以下 pubspec 的規則:

  • 將 SDK 的最低限制設定為你測試過的最低穩定版本(至少是 2.12.0)。

  • 所有的直接依賴都使用穩定版本。

歡迎使用空安全

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. You should see output like this when running or compiling your code:

Compiling with sound null safety

如果你走到了這一步,你應該已經完全將你的 Dart package 遷移至空安全了。當你的所有依賴也都完成了遷移,那麼你的程式就是健全的,同時可以正確處理空參考的錯誤。

謹代表 Dart 團隊的所有成員,感謝你 遷移你的程式碼。