「同じアプリを iPhone と Android の両方に出したい。ネイティブで2回作る?それとも Flutter で1回?」——よくある悩みです。今回は 全く同じ「天気アプリ風」を、iOS(Swift)・Android(Kotlin)・Flutter(Dart)の3通りで実際に作って、作り方・コード・見た目を比べました。さらに「シミュレータでどこまで検証でき、何が実機必須なのか」も実際に試してまとめます。
・iOS / Android / Flutter で同じアプリを作る手順とコードの違い(画像付き)
・3つの見た目を並べて比較
・コード量・1コードで両OS対応できるか 等の比較表
・シミュレータで「できること・できないこと」の早見表(Bluetooth・カメラ・位置情報など)
作るアプリ:おてんき比較
題材は API不要・ダミーデータの天気アプリです(キー管理不要で安全・再現しやすい)。3環境で全く同じ画面・データ・操作を作ります。
- 下タブ2つ:「天気」「設定」
- 天気タブ:5都市のリスト → タップで詳細画面へ。引っ張って更新(プルリフレッシュ)
- 詳細画面:気温・天気・湿度・風速・3日間予報
- 設定タブ:℃/℉切替トグル(アプリ全体の表示単位が変わる=状態管理の題材)
今回の環境
| 環境 | 言語 / UI | バージョン |
|---|---|---|
| iOS | Swift 6 / SwiftUI | Xcode 26.5・iPhone 17 シミュレータ |
| Android | Kotlin / Jetpack Compose | Android Studio・Pixel 7(API 36)エミュレータ |
| Flutter | Dart / Material | Flutter 3.44・iOS/Android 両対応 |
※ Bundle ID/applicationId は com.eight.weathercompare 系で統一。撮影は環境ごとに個別に行いました。
① iOS版(Swift / SwiftUI)の作り方
iOSは SwiftUI で「画面の見た目をそのままコードで宣言」して作ります。都市リストは List と NavigationLink を並べるだけ。状態(℃/℉)は ObservableObject で全画面に共有します。
List(cities) { city in
NavigationLink(destination: DetailView(city: city)) {
HStack {
Image(systemName: city.icon)
Text(city.name); Spacer()
Text(tempText(city.tempC, fahrenheit: useF))
}
}
}



SwiftUIは Xcodeのプレビューで書きながら確認でき、見た目は iOS純正そのもの。Apple製の純正感を出すならいちばん素直です。
② Android版(Kotlin / Jetpack Compose)の作り方
Androidも今や Jetpack Compose という宣言的UI。考え方はSwiftUIとそっくりで、リストは LazyColumn に ListItem を並べます。見た目は Material 3 純正です。
LazyColumn {
itemsIndexed(cities) { idx, city ->
ListItem(
headlineContent = { Text(city.name) },
leadingContent = { Icon(weatherIcon(city.weather), null) },
trailingContent = { Text(tempText(city.tempC, useF)) },
modifier = Modifier.clickable { onClick(idx) }
)
}
}



SwiftUIを知っていれば、Composeは「言語が違うだけでほぼ同じ感覚」で書けました。
③ Flutter版(Dart)の作り方
Flutterは 1つのコードで iOS と Android の両方が動くのが最大の特徴。リストは ListView に ListTile を並べるだけです。
ここで知っておきたいのが——Flutter自体は専用のシミュレータを持ちません。アプリの動作確認は、iOSシミュレータ または Androidエミュレータに載せて行います(必要に応じてMacアプリやWebブラウザでも動かせます)。今回も、同じ1つのコードを Androidエミュレータと iOSシミュレータの両方に載せて確認しました。
ListView(children: [
for (final city in cities)
ListTile(
leading: Icon(weatherIcon(city.weather)),
title: Text(city.name),
trailing: Text(tempText(city.tempC, useF)),
onTap: () => Navigator.push(...DetailScreen(city)),
),
])



右が驚きポイント——左(Android)と同じコードのまま、iOSシミュレータでもそのまま動きました。1回書けば両OSに出せる手軽さは圧巻です。ただし見た目は両OSとも Material(Android寄り)に統一されるため、「iOSらしさ」は自分で寄せる必要があります。
見た目を並べて比較
同じ「都市リスト」を3環境で並べました。左からiOS / Android / Flutterです。



iOSはApple純正、AndroidとFlutterはMaterialの世界観。「各OSの純正感」ならネイティブ、「全OSで統一」ならFlutterという性格の違いがそのまま出ています。状態管理も全環境で動作——設定で華氏をONにすると、全画面の気温が一斉に℉へ切り替わりました。



実機の動きも並べてみた
同じ「一覧 → 都市をタップ → 詳細 → 戻る」の操作を3環境で撮りました。左からiOS / Android / Flutterです(個別に撮影)。
遷移アニメや操作の反応は、どの環境も実用上じゅうぶん滑らかでした。
制作の比較表
| 観点 | iOS(Swift) | Android(Kotlin) | Flutter(Dart) |
|---|---|---|---|
| UIの書き方 | SwiftUI(宣言的) | Compose(宣言的) | Widget(宣言的) |
| 1コードで両OS | iOSのみ | Androidのみ | ◎ iOS/Android両方 |
| 見た目の方向性 | iOS純正 | Material純正 | 全OSで統一(Material) |
| 書きながら確認 | Xcodeプレビュー | Studioプレビュー | ホットリロードが高速 |
| コード量(同じ画面) | 中 | 中 | 少なめ(1本で両OS) |
| 向いている人 | iOSに集中 | Androidに集中 | 両OSを手早く |
シミュレータで「できること・できないこと」
記事のもう一つの主役。実機を用意しなくてもシミュレータ/エミュレータでどこまで検証できるかを、実際に試してまとめました。
⭕ できること(実際に確認)
| 項目 | iOSシミュレータ | Androidエミュレータ | Flutter(各シミュ上) |
|---|---|---|---|
| UI・操作・画面遷移・プルリフレッシュ | ✅ | ✅ | ✅ |
| 画面回転(横向き) | ✅ | ✅ | ✅ |
| ダークモード・言語/地域切替 | ✅ | ✅ | ✅ |
| 位置情報の擬似設定 | ✅ | ✅ | ✅(プラグイン経由) |
| 通信環境の擬似化(低速・遅延) | ✅ | ✅ | ✅ |
| ネット通信(サーバー経由) | ✅ | ✅ | ✅ |
例として画面回転も問題なし。横向きでもレイアウトが追従しました。
❌ できない/実機が必要
| 項目 | 理由 | iOS | Android | Flutter |
|---|---|---|---|---|
| Bluetooth / BLE機器連携 | シミュレータにBluetoothハードが無い | ❌ 実機必須 | ❌ 実機必須 | ❌ 実機必須 |
| セルラー回線・SMS・通話 | iOSは非対応/Androidは擬似可 | ❌ 実機必須 | △ 擬似可 | 載せた先に準ずる |
| 実カメラ(実写) | 物理カメラ非搭載 | ❌(ダミー可) | △(仮想カメラ) | ❌(ダミー可) |
| 本番のアプリ内課金 | サンドボックス/実機が必要 | ❌ 実機必須 | ❌ 実機必須 | ❌ 実機必須 |
・UI・画面遷移・ネット通信(サーバー経由)の検証 → シミュレータで十分
・Bluetooth機器・電話/SMS・カメラ実写・本番課金 → 実機が必要
ちなみに「擬似」が得意なのはAndroidエミュレータ(SMS・電波・着信まで設定可)。iOSは外観/位置/回線条件は充実ですがセルラー系は実機必須です。
🐦 Flutterの注意:Flutterは専用のシミュレータを持ちません。iOSシミュレータ/Androidエミュレータ(またはMac・Web)の上で動くので、可否は「載せた先のシミュレータ」に準じます(=ネイティブと同じ制約。Bluetoothやカメラ実写は同じく実機必須)。一方でUI確認だけならMac/Webでも動かせるぶん、より手軽でもあります。
結論:初心者はどれを選ぶ?
- 両OSに手早く出したい → Flutter。1コードで iPhone と Android が動く達成感が速く、ホットリロードも快適。最初の1本におすすめ。
- iPhone(または Android)に集中&純正の使い心地 → ネイティブ(Swift / Kotlin)。各OSらしさはこちらが上。
- 検証は基本シミュレータでOK。ただしBluetooth・カメラ実写・電話/SMS・本番課金を使うアプリは、早い段階で実機も用意しておくと安心です。
「同じアプリ」を3通りで作ってみると、考え方(宣言的UI)は驚くほど似ていて、どれか1つ覚えれば他にも移りやすいと感じました。まずは興味のある1つから触ってみてください。
それでは、今回はここまで。最後までありがとうございました😊