開発Tips

Flutter化でハマった不具合と解消③: Riverpod 3.x / ObjectBox 5.x のメジャー移行が重なった話

Flutter 移行の作業中、Riverpod 3.x・ObjectBox 5.x・build_runner のメジャーアップデートが同時期に重なりました(関連対応: riverpod_annotation / objectbox_generator / build_runner の一括移行)。結果として、生成コードとランタイムの噛み合わせが崩れ、ビルドが通らない状態にしばらく手こずりました。この記事はその教訓です。

何が噛み合わなくなるのか

Riverpod(@riverpod によるコード生成)と ObjectBox(@Entity からの生成)は、どちらも build_runner による生成コードに依存しています。

riverpod_annotation ──> *.g.dart (Provider)      ┐
                                                  ├─ build_runner が生成
objectbox_generator ──> objectbox.g.dart 等       ┘

ここで、riverpod(ランタイム)と riverpod_generator(生成器)、objectbox(ランタイム)と objectbox_generator(生成器)のバージョンがずれると、

  • 生成器が新 API 形式のコードを吐くのに、ランタイムが旧 API のまま
  • あるいはその逆

となり、生成コードがコンパイルできない/実行時に食い違うという状態に陥ります。「動いているものから1つずつ慎重に上げる」という定石が、ここでは逆効果になりました。

対処: 関連パッケージを1セットとして上げる

結論はシンプルで、コード生成に関わるライブラリは、対応バージョンを揃えて一度に上げることでした。

  1. 対応表を先に作る。 riverpod / flutter_riverpod / riverpod_annotation / riverpod_generatorobjectbox / objectbox_generator / objectbox_flutter_libs、そして build_runner の互換バージョンを、各パッケージの changelog から突き合わせる。

  2. まとめて pubspec.yaml を更新する。

  3. 生成物を作り直す。

    dart run build_runner build --delete-conflicting-outputs
    

    --delete-conflicting-outputs は、古い生成物と新しい生成物の衝突を消してから作り直すためのフラグ。メジャー移行では必須級です。

  4. 破壊的変更を潰す。 API リネームやシグネチャ変更をコンパイルエラーに沿って直す。

つまずきやすい点

  • 生成器だけ上げてランタイムを据え置く。 これが最もつまずきやすい。生成器とランタイムは必ずペアで上げる。
  • 古い .g.dart が残る。 --delete-conflicting-outputs を付けずに再生成すると、古い生成物が残って原因不明のコンパイルエラーになる。
  • 一度に上げる範囲を欲張りすぎる。 生成系は一括、それ以外の無関係な依存は別コミットに分けると、切り分けが楽。

まとめ

「依存は一つずつ慎重に」は一般には正しい原則ですが、生成器とランタイムが密結合したライブラリ群では例外です。対応表を先に作り、関連パッケージを1セットとしてまとめて上げ、生成物を作り直す——この順序を守るだけで、噛み合わせの問題の大半は避けられました。