経歴のスタート地点
中村あおいさんが初めてAndroid Studioを開いたのは新卒1年目の春、まだJavaが主流だった時代だ。最初に書いたのはRecyclerViewのアダプタで、ViewHolderの再利用パターンを上司に何度も叱られながら覚えた。3年目にはチーム内でKotlin移行をリードし、5年目にはJetpack Compose導入プロジェクトの技術選定を任された。
MVVM + Repositoryをベースにしたアーキテクチャ設計には自信があった。CoroutinesとFlowでの非同期処理、Hiltによる依存性注入、UI層と状態管理の分離——いずれも社内の若手エンジニアに教えてきた領域だ。一方で、業務でiOSを書いた経験はゼロ。SwiftUIのコードを読むことはできても、Xcodeのプロジェクト構造には今でも少し怯む。
最初のつまずき
Flutterに本格的に手を出したのは、社内のサイドプロジェクトとして「軽量な社内ツールアプリ」をiOS/Android両対応で作ることになったときだ。1週間あれば動くものができるだろう、と踏んでいた。実際には、最初のチュートリアルでつまずいた。
Widgetがすべてである、という設計思想にまず驚いた。AndroidのViewは画面に置く部品だが、Flutterではパディングも、配置も、状態管理も、すべてがWidgetのツリーとして表現される。慣れたMVVMの型紙をそのまま当てようとすると、build()メソッドの中に巨大なネストが生まれて読めなくなった。
もう一つの壁はDartの非同期処理だった。Kotlinのsuspend関数とCoroutineScopeの感覚で書くと、async/awaitとFutureの返り値の扱いがどこかで噛み合わない。FutureBuilderとStreamBuilderの使い分けを腹落ちさせるまでに、3週間ほど試行錯誤を繰り返した。
転機となった個人開発
転機は、業務ではなく個人開発で訪れた。「ランニング中に音声で記録するアプリ」を題材に、自分専用のFlutterアプリを一つ完成させようと決めた。要件を絞り込み、状態管理はRiverpodに固定し、画面数は5枚以内。週末ごとに少しずつ進めて、3ヶ月後にApp StoreとGoogle Playの両方に同時リリースした。
リリース当日、自分のiPhoneとPixelに同じビルドが届いた瞬間、それまでの違和感が一気に解けた。Widgetツリーは難解な構文ではなく、宣言的UIの素直な表現だと体で理解できた。Dartの非同期処理も、Streamを「時間軸を持つ値の流れ」と捉え直したことで、なぜFutureとStreamが分かれているのかが腑に落ちた。
会社に戻ってからは、Flutter案件に手を挙げ、既存のAndroidアプリを段階的にFlutter移行する計画書を書いた。Android時代に培ったアーキテクチャの感覚は、Flutterでもそのまま武器になった。MVVMはRiverpod + Notifierで再現でき、Repositoryパターンはそのまま使えた。
いま振り返る選書
最初の入口は『Flutter実践入門』だった。手を動かしながら読める構成で、Widgetツリーの感覚を掴むのに最適だった。網羅的なリファレンスとして手元に置いたのは『Flutterモバイルアプリ開発バイブル』で、迷ったときに辞書として開く一冊になった。
アーキテクチャの設計に踏み込む段階では『プロフェッショナルFlutter開発』が大きく効いた。状態管理ライブラリの比較と、複数画面にまたがる依存性管理の設計指針が整理されており、業務移行の根拠資料としても使えた。Dartそのものを言語として深掘りしたいタイミングで『Dart入門』を読み、async/awaitとStreamの設計思想に納得感が生まれた。モバイルアーキテクチャ全般を俯瞰するために『モバイルアプリアーキテクチャ』も併読した。
これから挑む人へ
中村さんは、ネイティブからFlutterへの越境は「言語の学習」ではなく「思考フレームの切り替え」だと表現する。Viewを置くのではなく、状態を宣言する。命令的に画面を更新するのではなく、状態が変わったらUIが追従する。この切り替えに体を慣らすには、業務とは別の小さな個人アプリを1本作り切るのが一番だ。
クロスプラットフォーム対応は、技術選定の話に見えて、エンジニアのキャリアの可動域を広げる選択でもある。AndroidかiOSか、ネイティブかクロスかの二択ではなく、両方を行き来できる足腰を持つことが、これからのモバイルエンジニアの強みになっていくはずだ。中村さんはこの数年で、選択肢を増やすことの安心感を実感している。