「まちのコイン」で使っているFlutterのバージョンを3系にアップデートした話

こんにちは。ちいき資本主義事業部でFlutterエンジニアをやっている植田です。
この記事ではFlutterで開発している「まちのコイン」というアプリをFlutter2系からFlutter3系にバージョンアップした事例について紹介します。

まちのコインとは?

「まちのコイン」はその地域に関わるユーザーがイベントやお店を通じて地域の人たちと会話をするきっかけを提供するプラットフォームです。

coin.machino.co

例えばQR読み取り機能やマップ機能といったものがあります。
QR読み取り機能ではQRを読み込んで地域限定のイベントに参加したり、スポットにチェックインしてコインを貯めたりすることができます。 マップ機能では利用できるスポットを確認できたり、スタンプラリーのコースを確認しながらまちを散策したりすることもできます。 また、利用するにはユーザー登録が必須となるので登録・ログイン、退会の機能も備えています。

「まちのコイン」を利用するスポット向けの管理アプリも用意しています。
そちらではユーザー情報のQRをカメラで読み込んで承認したりイベントを作成したりといったことができます。
内部的には2つのアプリをflavorを切り替えることで実現しているため一つのリポジトリで構成されています。

ユーザー管理にはFirebase Auth、クラッシュレポートにはFirebase Crashlyticsなども使っているため firebase_auth, firebase_crashlytics などのパッケージに強く依存した作りになっています。

Flutterをアップデートしていくモチベーション

Flutterには便利なパッケージがたくさんあります。
そして、そのほとんどがFlutterのバージョンアップに追従して新機能が追加したりbugfixを出したりしますが、中には全くアップデートされず陳腐化していくものもあります。

そのため、アプリのFlutterバージョンを随時アップデート対応しておかないと、パッケージの新機能やbugfixを取り込みたくてもFlutterのバージョンが古くて取り込めない、という問題が発生してしまいます。 Flutterのアップデート(特にメジャーアップデートは大変)もして、利用しているパッケージもアップデートして…諸々やると工数は膨れ上がります。 日頃からFlutterを最新バージョンに対応しておくことで、こういった運用工数を分散して割く事ができるようになります。

もちろん運用面での工数が減らせるだけでなくFlutterのアップデートをするだけで嬉しいこともいくつかあります。
Flutter本体のbugfixや言語としての新機能が使えるようになるので安定性の向上や、より柔軟なコードが書けるようになるかもしれません。 画面描画のパフォーマンスがより効率的になったり、メモリの使用率が下がったりといったパフォーマンス改善も入ってくる事が多いのでできるだけ最新のバージョンを追従する意識を持っていた方が良いでしょう。

Flutter3では大きくみると下記のようなパフォーマンス改善も入っていたのでユーザーにとってもよりアプリが快適に使えるのでメリットがあると思います。 2つのアプリを運用しているのでメリットも2倍です。

  • iOSでの120hzリフレッシュレートに対応
  • フレームビルドのパフォーマンスが約20%向上

今回のゴール

Flutterのバージョンアップの対応の中でやりたかったことは以下の2つです。

  • Flutterのバージョンを3.x系にアップデートする
    • 対応当時は2.10.5から3.3.3にアップデート
  • 依存しているパッケージを最新のバージョンまでアップデートする

つまづいたポイント

アップデートしていくにあたって事前に影響範囲の調査をしながら進めていましたが、想定していた以外のところで時間がかかってしまったポイントがあったのでいくつか紹介したいと思います。

docs.flutter.dev

docs.flutter.dev

flutter_facebook_authのセットアップ方法に変更があった

Flutterのアップデート後、iOSアプリが起動後すぐにクラッシュするという挙動が発生するようになりました。 以下のように、アプリ起動後すぐに接続が切れているログしか観測できなくてFlutter側の不具合なのかと思い調査にかなり時間を費やしてしまいました。

[        ] Waiting for observatory port to be available...
[+3339 ms] Observatory URL on device: http://127.0.0.1:58503/5oeggSOiUdw=/
[   +8 ms] Caching compiled dill
[ +139 ms] Connecting to service protocol: http://127.0.0.1:58503/5oeggSOiUdw=/
[ +418 ms] Launching a Dart Developer Service (DDS) instance at http://127.0.0.1:0, connecting to VM service at http://127.0.0.1:58503/5oeggSOiUdw=/.
[ +255 ms] DDS is listening at http://127.0.0.1:58514/ojIHBhq-rfk=/.
[  +46 ms] Fail to connect to service protocol: http://127.0.0.1:58503/5oeggSOiUdw=/: Exception: DDS shut down too early
[        ] Error connecting to the service protocol: failed to connect to http://127.0.0.1:58503/5oeggSOiUdw=/

エラーログの情報が足りないときはXcode側のログを確認してみると何かヒントになるかもしれません。
今回はXcodeに残っていたエラーログを確認すると FBSDKCoreKit が含まれている事がわかったので、 アプリ内でFacebook機能を利用している flutter_facebook_auth に絞って調査することで対応方法が見つかりました。

原因は、flutter_facebook_authのバージョンを4系にアップデートしたことで、flutter_facebook_authが内部で使っているFacebook SDKのバージョンも14にアップデートされていることでした。 Facebook SDKのセットアップ手順にもあるように、 FacebookClientToken の設定を追加する必要があり、それによってクラッシュは解消されました。

flare_flutterが非アクティブな状態になっている

「まちのコイン」の一部のアニメーションにはRive(旧Flare)のアニメーションが採用されていました。
こちらは .flr でアニメーションを作ってベクターアニメーションを描画できるという利点がありましたが、 すでにリポジトリが非アクティブとなっていること、新規アニメーションを作るときにRiveコンソールからアニメーション作成できる人がいないという問題がありこのタイミングで脱却することになりました。

Riveアニメーションを使っているところはFlutterの AnimationController を使った実装に置き換えたり、複雑なものはAPNGに置き換えたりして対応する方針としました。

その他FlutterのBreaking Changes

Scrollbar 'isAlwaysShown' is deprecated and shouldn't be used. Use thumbVisibility instead. This feature was deprecated after v2.9.0-1.0.pre..
Try replacing the use of the deprecated member with the replacement.

スクロールバーを常に表示しておく必要がある場合は、 isAlwaysShownthumbVisibility に置き換えるだけで対応が完了します。

WidgetsBinding.instance is non null value

WidgetsBinding.instance?.addPostFrameCallback は非同期処理が完了した時にUIを更新したい場合などに使うケースが多いと思います。 non nullになっただけなので修正は簡単ですが、これを頻繁に使っているアプリは修正箇所が多くて大変かもしれません。

TextInputClient Missing concrete implementations of 'TextInputClient.insertTextPlaceholder', 'TextInputClient.removeTextPlaceholder', and 'TextInputClient.showToolbar'.
Try implementing the missing methods, or make the class abstract.

TextInputの挙動を拡張などしたい場合に利用する TextInputClient にメソッドが追加されていました。 TextInputClientをimplementsしているWidgetがあれば追加されたメソッドを実装する必要がありますが、実装は空のままでも問題ないようです。 メソッドの定義だけ追加しておきましょう。

やって良かったことなど

Flutterのバージョンが古いことで一番困っていたのは不具合の修正を取り込めないということでしたが、Flutterをアップデートすることでパッケージ側の変更も取り込むことができるようになりました。 特にログイン周りで使用しているFirebase Authの不具合修正を取り込めるようになったことが大きかったですね。 描画パフォーマンスの向上や言語機能としての新機能も嬉しい要素で、Enhanced enumsfreezed パッケージで実装している箇所の置き換えもしていけそうです。

  • 依存パッケージのbugfixを取り込めるようになった
  • devtoolsで描画パフォーマンスの向上を確認できた
  • Enhanced enumsが使えるようになった

packageの更新にDependabotを導入した

今回はFlutterをアップデートするのでアプリの全体テストを実施する必要がありました。
そこにパッケージのアップデートも合わせて対応しましたが、大幅にアップデートされているパッケージもあればアップデートが止まってほぼdeprecatedなパッケージもあり、苦戦することが多かったです。 こうした影響範囲の広い改修のタイミングで対応するよりも小さくアップデートを取り込んでいった方がテスト範囲も小さくなりますし、 1つ1つをアップデートする工数も小さくて済みそうです。

「まちのコイン」のアプリは多くのパッケージを利用していますのでbugfixがあれば積極的に取り込んでいきたいです。
現在はbeta版ではありますが、pub用のDependabotを設定してパッケージのアップデートがあれば適宜取り込む運用で開発を進めています。 特にFirebaseなどのパッケージ間で依存関係があるもの、他のFirebaseパッケージが依存している firebase_core を依存解決する必要がある場合に便利で 他のパッケージが更新されたときにまとめてアップデートしてくれます。

slack notification from dependabot

まとめ

  • Flutterを継続的にバージョンアップしていくことでFlutterやパッケージのbugfixを安定して取り込むことができるようになる
  • アプリの描画パフォーマンスがいい感じに上がる
  • パッケージの更新にはDependabotを使うと依存関係含めて常にアップデートし続けることができるので便利

カヤックでは、信頼できる開発環境運用に興味があるエンジニアも募集しています。