メインコンテンツにスキップ

Null safety への移行

このページでは、コードを null safety に移行する方法と時期について説明します。所有する各パッケージを移行するための基本的な手順は次のとおりです。

  1. 依存しているパッケージが移行するのを待ちます
  2. パッケージのコードを移行します。できれば、インタラクティブな移行ツールを使用します。
  3. パッケージのコードを静的に分析します
  4. 変更が機能することを確認するためにテストします
  5. パッケージがすでに pub.dev にある場合は、null safe バージョンをプレリリースバージョンとして公開します

移行ツールの使用経験を非公式に確認するには、このビデオをご覧ください。

新しいタブで YouTube で視聴する:「Dart パッケージを null safety に移行する方法」

1. 移行を待つ

#

依存関係グラフのリーフから順に移行するなど、コードを順番に移行することを強くお勧めします。たとえば、パッケージ C がパッケージ B に依存し、B がパッケージ A に依存している場合、まず A を null safety に移行し、次に B、次に C を移行する必要があります。

Illustration of C/B/A sentence

依存関係が null safety をサポートする前に移行できますが、依存関係が移行する際にコードを変更する必要がある場合があります。たとえば、関数が null 非許容パラメータを受け取ると予測していても、パッケージがそれを非 null 非許容に変更した場合、null 非許容引数を渡すとコンパイルエラーになります。

このセクションでは、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 safe なリリースが存在するかどうかを確認します。

これは、単純なパッケージの出力例です。各パッケージの緑色のチェックマークが付いたバージョンは null safety をサポートしています。

Output of dart pub outdated

出力は、パッケージの依存関係すべてに null safety をサポートする解決可能なプレリリースがあることを示しています。

パッケージの依存関係のいずれかがまだ null safety をサポートしていない場合は、パッケージの所有者に連絡することをお勧めします。連絡先は、pub.dev のパッケージページで見つけることができます。

依存関係を更新する

#

パッケージのコードを移行する前に、依存関係を null safe なバージョンに更新します。

  1. null safety をサポートする最新バージョンにアップグレードするには、dart pub upgrade --null-safety を実行します。注意: このコマンドは pubspec.yaml ファイルを変更します。

  2. dart pub get を実行します。

2. 移行する

#

コードを null safe にするために必要な変更のほとんどは、簡単に予測できます。たとえば、変数が null になりうる場合、その型には ? サフィックスが必要です。名前付きパラメータが null 非許容であってはならない場合は、required とマークするか、デフォルト値を指定します。

移行には 2 つのオプションがあります。

移行ツールを使用する

#

移行ツールは、null 非安全な Dart コードのパッケージを受け取り、それを null safety に変換します。Dart コードにヒントマーカーを追加することで、ツールの変換をガイドできます。

ツールを開始する前に、準備ができていることを確認してください。

  • Dart SDK の 2.19.6 リリースを使用します。
  • dart pub outdated --mode=null-safety を使用して、すべての依存関係が null safe で最新であることを確認します。

パッケージの 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

各変数と型注釈について、ツールが推論する null 非許容性を確認できます。たとえば、前のスクリーンショットでは、ツールは行 1 の ints リスト(以前は int のリスト)が null 非許容であると推論し、したがって 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 になりえないことがわかっているため、移行結果を改善できます。

移行結果の改善

#

分析が間違った null 非許容性を推論した場合、一時的なヒントマーカーを挿入することで、提案された編集をオーバーライドできます。

  • 移行ツールの Edit Details ペインで、Add /*?*/ hint および Add /*!*/ hint ボタンを使用してヒントマーカーを挿入できます。

    これらのボタンはファイルに直接コメントを追加し、元に戻すことはできません。ツールが挿入したヒントが不要な場合は、通常のコードエディタを使用して削除できます。

  • ツールが実行中でも、エディタを使用してヒントマーカーを追加できます。コードはまだ null safety をオプトインしていないため、新しい null safety 機能を使用できません。ただし、null safety 機能に依存しないリファクタリングなどの変更は行えます。

    コードの編集が完了したら、Rerun from sources をクリックして変更を反映させます。

次の表は、移行ツールの提案された編集を変更するために使用できるヒントマーカーを示しています。

ヒントマーカー移行ツールへの影響
expression /!/移行されたコードに ! を追加し、expression をその基になる非 null 非許容型にキャストします。
type /!/type を非 null 非許容としてマークします。
/*?*/直前の型を null 非許容としてマークします。
/*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 のバージョンコメントを除いて変更されません。

増分移行の詳細については、Unsound null safety を参照してください。

完全に移行されたアプリとパッケージのみが Dart 3 と互換性があることに注意してください。

変更の適用

#

移行ツールが提案するすべての変更が気に入った場合は、Apply migration をクリックします。移行ツールはヒントマーカーを削除し、移行されたコードを保存します。ツールは pubspec の最小 SDK 制約も更新し、パッケージを null safety にオプトインします。

次のステップはコードの静的分析です。有効であれば、コードをテストします。次に、pub.dev でコードを公開した場合は、null safe なプレリリースを公開します。

手動で移行する

#

移行ツールを使用しない場合は、手動で移行できます。

まずリーフライブラリ(パッケージの他のファイルをインポートしないライブラリ)を移行し、次にリーフライブラリに直接依存するライブラリを移行することをお勧めします。最後に、パッケージ内の依存関係が最も多いライブラリを移行します。

たとえば、lib/src/util.dart というファイルがあり、他の(null safe な)パッケージとコアライブラリをインポートしますが、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 に設定され、すべてが null safety にオプトインされます。

  3. IDE でパッケージを開きます。
    多数の分析エラーが表示される可能性があります。それは正常です。

  4. アナライザーを使用して静的エラーを特定しながら、各 Dart ファイルのコードを移行します。
    必要に応じて、?!requiredlate を追加して静的エラーを解消します。

手動でのコード移行に関するヘルプについては、Unsound 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.0 または 1.0.0 です。

pubspec を確認する

#

パッケージの安定した null safe バージョンを公開する前に、次の pubspec ルールに従うことを強くお勧めします。

  • Dart の下限 SDK 制約を、テストした最小の安定バージョン(少なくとも 2.12.0)に設定します。
  • すべての直接依存関係の安定バージョンを使用します。

サンプルとドキュメントを更新する

#

まだ行っていない場合は、すべてのサンプルをパッケージの移行済みバージョンを使用するように更新し、null safety にオプトインします。

パッケージの個別のドキュメントまたはチュートリアルを公開した場合は、それらも null safe リリースに合わせて最新の状態になっていることを確認してください。

null safety へようこそ

#

ここまで到達した場合は、完全に移行された null safe な Dart パッケージができているはずです。

依存しているすべてのパッケージも移行されている場合、プログラムは null 参照エラーに関して sound です。コードを実行またはコンパイルすると、次のような出力が表示されるはずです。

Compiling with sound null safety

Dart チーム一同、コードを移行していただきありがとうございます。