Dartの自作 package を null safety 版に移行した際の知見


自作 package を 3 つ null safety 版に移行した際の知見のメモです。

Null safety in Flutter - Flutter

Null safety に関する公式資料。

https://flutter.dev/docs/null-safety

migration-guide

Null safety への移行ガイド

https://dart.dev/null-safety/migration-guide


上記移行ガイドに従えば移行できます。

小規模な package ならば、いろいろ確認しつつもすぐに移行できます。私は事前知識を仕入れていたこともありますが、最初の package の移行には 30 分かかりませんでした。ただし、依存している package がまだ null safety 対応していないならば、移行できません。(正確には、mixed-version programs にすると移行できるようですが、推奨されていません)

2020/11/21 現在、Flutter フレームワークと Dart コアライブラリは移行完了していますが、他のほとんどの package はまだ移行していません。

依存 package たちの移行状況は、以下のコマンドで確認できます。

dart pub outdated --mode=null-safety

dart migrate

dart migrateコマンドを使うと、interactive migrator app が web app として立ち上がり、どのようにコードを移行するかの差分を確認できます。そして、移行ボタン押下でコードを null safety なものに一括変換できます。ただし、どのコードベースでも最適な移行提案をするというわけではないようです。私の package の場合は、late final にすべきと意図していた箇所について、型注釈に?!接尾辞をつける提案になってました。

これに関しては、いったん dart migrate で自動一括変換しておいて、それから git の差分を見つつ、late や late final に変換すべき箇所があれば手動で書き換えていくのが一番効率が良いと思います。late や late final への手動書き換えの最中に、Dart analyzer が 書き換えに伴う?!の削除をすべき箇所を警告してくれますので、楽です。

late final への移行なども考慮しつついろいろ確認していくと、私のひとつの package の場合は移行に 3 時間くらいかかりました。

Null safety version と Legacy version

Null safety version は、pre-release version として publish します。

新機能を追加する場合は、従来の version (Legacy version と表現されています) と、null safety version のふたつの version へそれぞれ追加することも考えられますが、私は省力化のため、legacy version への新規機能追加を止めて null safety version のみに追加していく方針です。package:test もその方針のようです。

リポジトリでは、私は両方の version のブランチを併存させているものもありますし、そうする必要もないと判断したものは main branch に merge させています。

mixed-version programs

ひとつの プログラム に、Null safety 対応 library と、未対応の Legacy version の library を混在させることができます。library ごとに少しづつ(incrementally) null safety version に移行することができます。以下のページで解説されています。

Unsound null safety | Dart

https://dart.dev/null-safety/unsound-null-safety

注意点として、Legacy version の package への依存がある package を null safety version に移行するためにこの手法を適用するのは、できるだけ避けることが推奨されています。

私は、ひとつの package を移行する際、その example のひとつに、 Firebase 関連 package などまだ null safety 版に移行していない package への依存がある Flutter アプリがあり、この手法の適用を試しました。結局、Legacy library への依存がある library にはすべて、// @dart=2.9といったコメントをつけることで、動作することを確認しました。そうしないと、起動直後に異常終了してしまいました。

その example アプリは以下です。

https://github.com/polyflection/reactive_component/tree/null-safety/example/composing_firebase

Flutter アプリの null safety 版への移行は、Firebase や RxDart などの人気パッケージが移行するまでは待つのがやはり無難だと思います。来春予定の stable 版がリリースされてから、一気に null safety 版に書き換えるのが一番手間が少ないでしょう。