はじめに
昨年に引き続き、今年もFlutterのカンファレンス、FlutterKaigi2024に参加してきました。
今年は11月21日と22日の2日間にわたり開催され、例年以上に多くのセッションに参加することができました。
会場は有明セントラルタワーホール&カンファレンスで、セッションは2つのホールで行われました。スポンサーブースも2箇所に設置され、会場は非常に賑やかでした。
今年もFlutter製でFlutterkaigiのアプリが作成されており、リポジトリが公開されているのでこちらからも多くの学びが得られそうです。
どのセッションも非常に学びが多く素晴らしいものでしたが、中でも特に印象深かったセッションについての感想を綴ろうと思います。
印象に残ったセッション
出前館アプリにおけるFlutterアプリ設計とそれを支えるCICD環境の進化
元々React Nativeで作成されていた出前館のクライアントアプリをflutter化するにあたり、どういったフォルダ構成やアセット管理また状態管理をどのように行うかの発表がなされていました。
まずフォルダ構成として
- Type/Domain
- Feature
- Package
この三つが候補としてあげられており、プロジェクトの規模等を考えてFeature型を採用されたようでした。弊社も元々はType/Domain型で運用しておりましたが、プロジェクトの規模が大きくなるにつれファイルを検索しずらい等の問題が発生したためFeature型に移行した経緯があります。
アセット管理にはflutter_genを使っているとのことでした。BuildContextをextensionしてcontext経由でアセットへアクセスしているという工夫もお話しされていました。弊社でもflutter_genを採用しており、flutter_genが自動生成するSvgGenImage, AssetGenImageをextensionしてなるべく不要な引数やデフォルト引数を設定することでアセットへのアクセスを簡易にする工夫をしています。アセット管理に関するベストプラクティスについて、本発表を機会に今後もっと追求していければと思いました。
状態管理にはBLoCを状態を示すStateにはFreezedを採用されているとのことでした。BLoCについては未経験だったためどういった使い方をするのかを知れてよかったです。Freezedは弊社でも採用しており利便性の高さは共感するところでありました。
最後にCI/CD環境についてお話しされていました。当初はBitriseを採用するところから始まったというお話しからはじまり、BtoBアプリのflutter化に追従する形でTeamCityへ移行されていったというお話しをされていました。複数チームが存在する中で、オンプレ環境でバージョンの異なるビルド環境でCI/CDをしていくことで発生する苦労があることを知れました。
発表概要
発表資料
気をつけたい!Desktop対応で陥りやすい罠とその対策
本セッションではDesktop対応をするにあたり事前に知っておかなければ陥ってしまう特有の罠とその対策についてお話しされていました。Desktop対応とありますが、Webも含む広義の意味で使用されています。なのでPadやブラウザ等のマルチデバイスでのレスポンシブ対応における内容も含まれていました。
まず文字入力時の文字数制限がアプリでは問題なかったものがWebだと入力言語を切り替えた際に制限できなくなる現象についてお話しされていました。こちらはTextFieldのmaxLengthEnforcementにenforcedを指定することで解決できるようでした。知らなければ絶対に陥ってしまう罠なので本発表でTipsを知れてよかったです。
次にスクロール可能なWidgetがWeb対応することによってできなくなる現象についてお話しされていました。こちらはマウスによるドラッグを有効にするプロパティを明示的に指定することで解決されたとのことです。アプリ版のみであればタッチによるドラッグしか考えなくてよいですが、デスクトップ等の対応をする場合はマウスによるドラッグも念頭においておく必要があることが知れました。
次に画面レイアウトに関わるレスポンシブ対応についてお話しされていました。 BottomNavigationBarをWebで利用するとタブ間のすき間が大きくなり使いづらくなってしまうため、左側にメニュー一覧を表示することができるNavigationRailを採用されたとのことでした。Material3のWindow size classesを基準にBottomNavigationBarとNavigationRailを切り分けていました。
またNavigationRailの最下部にメニューを表示したいというデザイナーさんからの要望を実現するためにtrailingプロパティを利用して実現しようとしたが、想定していたものと全く違う表示になってしまい、それでも実現するために試行錯誤した結果コードがとても複雑化してしまったようです。 そこでデザイナーさんに相談した上でデザインを調整してもらい解決できたようです。この事例のようにflutter側では解決できないこと・解決しようとするととても複雑になってしまいメンテナンス性が損なわれてしまう場合には相談してデザインを変えることで解決するということは実際の現場では大切なことであると感じました。
最後にWeb特有の課題についてお話しされていました。アプリほどには成熟していないDesktop領域では実装してみると様々な課題にぶちあたることになり、その際にどういった行動をとればいいのかが明確になりました。
- flutterのissueを検索する
- Widgetのプロパティを詳しくみてみる
- 公式のドキュメント、MigrationGuideをちゃんと読む
とても基本的だとされていることですが、ちゃんとできてないことも多いので、本発表を期に改めて意識したいことだと思いました。
発表概要
発表資料
マッチングアプリ『Omiai 』のFlutterへのリプレイスの挑戦
Omiaiからは長年iOS, Androidそれぞれのプラットフォーム言語で開発されてきたアプリを技術的負債や仕様差異を解消するためにflutter化により解消していくお話しでした。リプレイス中にも既存アプリの機能追加は止めたくないということでAdd-to-Appによるアプローチをとっていたのは、弊社でも同様の経験があり当時のことを思い出させてくれるようでした。
リプレイスするにあたりマンパワーに依存することにより発生するコード規約違反や技術的負債の発生に対する対策として
- 依存関係を明確にするためマルチパッケージ構成をとる
- ユニットテストを十分に継続的に書く
- 実装内容をパターン化し新規参入者に対する学習難易度を下げる
といったことを徹底されているようでした。
依存関係が明確にされていることで、依存に関する誤った実装がそもそもできないというように仕組みで解決されていたので素晴らしいと思いました。また各レイヤーでの責務が明確化されておりユニットテストを書くのも容易になったということも学ぶことができました。
コードへのドキュメーションコメントを書くのも必須にしており、そのチェックにpublic_member_api_docsというlintルールを活用していました。コメントを必須にすることによってソースコードが「実行可能な仕様書」にしているというのが興味深かったです。
またVS Codeのスニペット機能をかなり活用していたのも興味深かったです。 これを機に弊社でもスニペット機能は活用していこうと考えております。
発表概要
発表資料
アニメーションを最深まで理解してパフォーマンスを向上させる
弊社のプロダクトでも各所にアニメーションを使っていますが、flutterでのアニメーションは若干特殊なところもあり、正しい実装なのか、他の代替実装やライブラリはあるのか、改善できる要素はあるのかなど、知見を増やすことを目的に本セッションに参加しました。
本セッションでは、flutterにおけるアニメーションの最新動向や入門解説、アニメーションの詳細な説明まで幅広く解説した、本セッションでflutterのアニメーションに関する知識を網羅的に学べる内容でした。
特にパフォーマンスの計測と分析のセクションでは、1000個のロゴを回転させたアプリを例に、パフォーマンス低下の原因とdevtoolsを使った改善方法の解説はとてもわかりやすく学びが多かったセッションでした。
発表概要
発表資料
実践的パッケージ戦略
弊社でも、既存プロダクトのマルチパッケージ化を検討しており、ぜひ知見を得たいと思い、本セッションに参加しました。
本セッションではクリーンアーキテクチャを例に、riverpodを使った簡単なコードで説明するセッションでした。
まず3つのレイヤーを使ったシンプルなプロジェクトを例に解説していました。
次に上記の実装を依存性逆転の原則(DIP)に従い、以下のように依存関係を変更した実装の手順を説明をいただきました。
依存関係を逆転すると、当然直接モジュールを参照していたところは壊れるため、
下位になったUseCaseは依存してたモジュールを使用する際に、インターフェースを追加してそれを介して呼ぶように修正します。
上記になったGatewayは、UseCaseのUserRepositoryの具象クラスを実装して、それを返すproviderを更新します。
最後に、AppのProviderScopeのoverridesに更新したproviderを設定して完成です。
しかし、依存してるモジュールの数が増えていくと、都度overridesに追加設定していく必要がでてくる問題が発生します。
そこで、Kyoheiさん自身が作成した override_pod というツールを使うことで、build_runnerで自動生成して、前述の問題を解決できるようです。
今回のようにレイヤーごとにパッケージに分けて、マルチパッケージな構成するにあたり、melosというツールの紹介がありました。melos をつかったマルチパッケージプロジェクトでそのままtestを実行すると、期待した正しいカバレッジが得られないことがあるため、Kyoheiさん自身が作成したlcov_excluderというツールを使って除外設定することで、melosと併せて使うと便利とのことでした。
普段業務で、依存関係をひっくり返すようなことはする機会がなかったですが、実際今回の発表を例にriverpodでの解決方法は非常に興味深く学びの多かったセッションでした。
発表概要
発表資料
Shorebirdを活用したFlutterアプリの即時アップデート:Code Pushの実践と可能性
Shorebird のCode Pushとは、Flutterをforkしたプロダクトで、Google Play StoreやAppStoreのアプリ審査フェーズをスキップして、Flutter アプリの変更差分をユーザに反映することができる夢のようなツール(サービス)です。
アプリリリースするまでに、必ず審査を通過しなくてはいけませんが、その審査結果はまちまちで、早くても半日〜1日かかってしまって、早くユーザに届けたいプロダクト側はいつももどかしい思いをした経験はあるのではないでしょうか?
その問題をこのShorebirdのCode Pushを使えば解消できる!?というものでした。
ただし、当サービスを利用するにあたって若干制約があるようでした
- flutter v3.24.0以上の環境が必要(意外と高い)
- 本家flutterでビルドしたアプリは不可
- dartコードの差分のみ適用可能(assetの追加削除やネイティブコードの差分が含まれる場合は通常通りの各ストアのリリースフロー)
- 各ストアのガイドラインには準拠(当然)
- ユーザ側はアプリを2回起動が必要
料金プランは、従量課金制で5,000パッチ(1端末1パッチ計算)までは無料プランで使用可能なので、まず気軽に使い始めてもよさそうですね!
発表概要
Flutterアプリで可用性を向上させたFeatureFlagの運用戦略とその方法
トランクベース開発を実現するための仕組みであるFeature Flag に関するセッションで、実際にプロダクトで直面した課題と、それを踏まえた実装方法や運用について解説されていました。
弊社でもFeatureFlagを導入しており、発表前からとても注目していたセッションの1つでした。
FeatureFlagは新機能の公開/非公開をリリース後でも簡単に切り替えができる仕組みで、これにより、以下のようなメリットが得られます。
- 開発中の機能をマージすることができる
- PRの粒度を小さくできる (コンフリクトを抑える、レビューコストの軽減)
- リリース後に大きな問題が起きてもすぐにロールバックできる (Firebase RemoteConfigなどとの連携)
- ABテストの実施が容易になる
発表の中で特に印象的だったのは、UI層でのFeatureFlagの扱い方で、FeatureFlagをラップしたFlagBuilderというWidgetを用いているという点です。
FlagBuilder は、onBuilder と offBuilder という2つのコールバックを持ち、引数に渡した FeatureFlag の値に応じて、それぞれのコールバックが呼ばれる仕組みになっているようです。
if 文や三項演算子を使って条件分岐を行うと、分岐が散らばったり、可読性が低下することがありますが、FlagBuilder 内に分岐を集約することで、この問題を解消できている点が非常に良いと感じました。
また、既存の実装に関しては、コピペで offBuilder に残す運用を採用することで、デグレの発生を抑える取り組みが行われているとのことでした。
ロジック面においても、Feature Flag による制御を基本的に domain レイヤー(UseCase) のみに制限することで、条件分岐が分散しないようになっているようです。
Danger を使ってレビュー時に機械的にFeature Flag の制御を忘れに気づけるようにしている点も非常に参考になりました。
発表概要
発表資料
DevTools Extensions で独自の DevTool を開発する
DevTools Extensions を使って独自のデバッグツールを作成する方法について、ライブコーディングを交えてとても詳しく解説されているセッションでした。
(NeoVimを使いこなしていてめちゃくちゃかっこよかったです 笑)
まず、 DevToolsに独自の機能を追加できることを知らなかったので、その点に驚きました。
セッションでは、DevTools Extensions を活用した具体的なツールとして、以下の3つが紹介されていました。
- GraphQL Cache Inspector
- GraphQLClientのキャッシュを可視化
- 正規化されたTree構造で出力
- Loading State Toggle
- Loading状態のUIへの切り替え
- Skeleton などのデザイン確認に使用
- Dio Logger Toggle
- Http通信のログ出力を ON/OFF
- ログの追跡を容易にする
dev toolの作り方は下記の3ステップで比較的簡単に作成できるようです。
- config.yamlの作成
- Devtools extensionsのパッケージを作成
- Flutter webでbuild
GraphQL Cache InspectorはReactのツールを参考に作成したとのことで、
どんなツールを作成するかのアイディアはWeb界隈のツールを参考にするのもオススメとのことでした。
DevTools Extensions を活用することで、アプリ内にデバッグ機能を埋め込むことなく、開発中のデバッグや日々の開発がより快適に行えるのは非常に嬉しいポイントですね!
発表概要
発表資料
Effective Form ~ Flutterによる複雑なフォーム開発の実践 ~
ユーザーからの入力を受け取るためのインターフェースであるフォームの実装方法や注意点について詳しく解説されたセッションでした。
まず、例として挙げられたのはログインフォームです。
このフォームでは、EmailとPasswordを入力値として受け取り、Loginボタンが表示される比較的シンプルな画面が想定されています。
フォームのフローは以下の通りです。
- UIの表示
- 入力状態の管理
- バリデーション
- Dirty管理
- Submit
特に注目したいのが、4番目のDirty管理です。
EmailのTextFieldにシンプルなバリデーションを適用した場合、初期状態でいきなりInvalid状態になってしまうという問題が紹介されました。
Dirty管理は、こうしたケースでバリデーション結果をユーザーにフィードバックするタイミングを適切に制御するための考え方とのことです。
Dirty管理のポイント
- 入力フィールドが変更されたタイミングを追跡
- ユーザーが変更を確定したタイミングで「dirty状態」と見なす
- エラー表示や変更確認ダイアログの表示タイミングを管理できる
このDirty管理を実現する方法として、formzというパッケージが紹介されました。
formz
パッケージには、FormzInput
という抽象クラスが用意されており、入力値を「pure」と「dirty」の2つの状態で管理できるようです。
TextFieldのonChanged
でpure状態に、onSubmitted
でdirty状態に切り替えることで、Dirty管理を実現できます。
また、validator
メソッドをオーバーライドすることで、バリデーションロジックを実装できます。
displayErrorの値を使うことで、dirty状態の場合のみエラーを表示することができ、エラー表示のタイミングを柔軟に制御できる点が非常に便利ですね!
さらに、より複雑なフォームにおける実装課題とその戦略についても解説がありました。
- 管理すべき状態が多い → Single Source of Truthの担保 (状態の一貫性が担保できる)
- 状態更新による多彩な動的制御が必要 (状態が決まれば挙動が決まる)
- 大量の機能と複雑な依存 (状態の管理範囲が最小化される)
弊社のアプリでも複数の場所でフォームを使用しているため、Dirty管理や設計戦略を踏まえて改善に活かしていきたいと思います。
発表概要
発表資料
最後に
冒頭でも触れましたが、今年は2日間にわたる開催で、内容が非常に充実していました。 どのセッションもとても多くの学びがあり、参加者の皆様の熱意にも多くの刺激をいただきました。 登壇者の皆様、そして運営スタッフの皆様、本当にありがとうございました。 来年のFlutterKaigiも楽しみにしています。