はじめに
個人で割り勘アプリ wa/ri を作った。iOS / Android / Web / LINE(LIFF)の4プラットフォームに対応している。
マルチプラットフォーム開発といえば、まず出てくる選択肢は「Flutter一本化」だろう。工数を最小化できるし、ロジックも共通化できる。個人開発なら特にそのメリットは大きい。
でも、wa/riではあえてその選択をしなかった。
iOS は Swift(SwiftUI)、Android / Web は Flutter という構成にした。その理由と、その選択のトレードオフについて書く。
構成の全体像
| プラットフォーム | 実装 | UIデザイン指針 |
|---|---|---|
| iOS | Swift / SwiftUI | Liquid Glass(Apple HIG準拠) |
| Android | Flutter | Material Design 3 |
| Web | Flutter(静的ビルド → nginx配信) | Material Design 3 |
| LINE(LIFF) | Webクライアントをそのまま再利用 | Material Design 3 |

なぜ一本化しなかったのか
「工数」より「ユーザーが感じる自然さ」を優先した
Flutter一本化の最大のメリットは工数削減だ。これは間違いない。
ただ、wa/riを作るにあたって一番避けたかったのは「使い始めるハードルの高さ」だった。登録不要、連携不要、広告なし──構造的な気軽さを徹底的に追求したアプリで、見た目だけが「なんか違う」という状態にしたくなかった。
iOSユーザーはAppleのHIGに沿ったUIに慣れている。AndroidユーザーはMaterial Designに慣れている。Flutterで両方をターゲットにすると、どちらにも「ちょっとだけ違和感がある」UIになりやすい。
特にiOSは、iOS 26から導入された Liquid Glass に代表されるように、Apple独自のデザイン言語が非常に強い。FlutterはこのあたりのネイティブUIとの乖離が出やすく、「なんかAndroidっぽいな」という印象をiOSユーザーに与えてしまう。
wa/riではその違和感をなくすことを選んだ。
実際にやってみてどうだったか
iOSをSwiftで実装するコスト
正直、工数は増える。ロジックを二重に持つわけではないが(後述)、UIの実装は完全に別物になる。
- SwiftUIのコンポーネント設計
- Liquid Glassに沿ったアニメーション・マテリアル表現
- iOS特有のナビゲーションパターン(NavigationStack など)
これを一人でやるのは、確かにしんどい部分もあった。
でも「ロジック集約」で救われた
wa/riではフロントエンド側で極力計算をさせない設計を採っている。割り勘の計算ロジックはすべてバックエンド(NestJS)に集約されており、クライアントはAPIを叩くだけだ。
これがマルチプラットフォームとの相性が非常に良かった。
- ロジックはバックエンドのテストだけで全クライアントの正しさを担保できる
- 同じ計算ロジックを各クライアントに重複実装しなくていい
- バックエンドを直せば全クライアントに即反映される(ストア審査やビルド待ちが不要)
UIは分けたが、ロジックは一本化した。この構造のおかげで、「Swift + Flutter」という一見コストの高い選択が、現実的に成立している。
この選択は「正解」か?
チーム開発や事業としてのプロダクトであれば、Flutter一本化が正解になるケースの方が多いと思う。
保守コストの分散、メンバーのスキルセット、リリーススピード──どれを取っても一本化に優位性がある。
ただ、個人開発で「自分が使いたいものを作る」という文脈では話が変わる。
- 意思決定に承認が要らない
- 技術的な挑戦そのものが目的のひとつ
- 「このUIで出したくない」という感覚を妥協しなくていい
wa/riはもともと自分用に作り始めたアプリだ。iOSネイティブのUIが好きで、その体験を犠牲にしたくなかった。それだけのことでもある。
まとめ
Flutter一本化しなかった理由を一言で言うなら、「誰に何を届けたいか」を技術選定の軸にしたからだ。
工数削減より、ユーザーが感じる「自然さ」を優先した。その選択を支えたのは、ロジックをバックエンドに集約するという設計の工夫だった。
技術選定に絶対の正解はない。ただ、「なぜその選択をしたのか」を自分の言葉で説明できる状態にしておくことは、個人開発でも、チーム開発でも、たぶん大事なことだと思っている。

No responses yet