「同じアプリを 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バージョン
iOSSwift 6 / SwiftUIXcode 26.5・iPhone 17 シミュレータ
AndroidKotlin / Jetpack ComposeAndroid Studio・Pixel 7(API 36)エミュレータ
FlutterDart / MaterialFlutter 3.44・iOS/Android 両対応

※ Bundle ID/applicationId は com.eight.weathercompare 系で統一。撮影は環境ごとに個別に行いました。

① iOS版(Swift / SwiftUI)の作り方

iOSは SwiftUI で「画面の見た目をそのままコードで宣言」して作ります。都市リストは ListNavigationLink を並べるだけ。状態(℃/℉)は 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))
    }
  }
}
iOS 都市リスト
リスト(℃)
iOS 詳細
詳細画面
iOS 設定
設定(℃/℉)

SwiftUIは Xcodeのプレビューで書きながら確認でき、見た目は iOS純正そのもの。Apple製の純正感を出すならいちばん素直です。

② Android版(Kotlin / Jetpack Compose)の作り方

Androidも今や Jetpack Compose という宣言的UI。考え方はSwiftUIとそっくりで、リストは LazyColumnListItem を並べます。見た目は 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) }
    )
  }
}
Android 都市リスト
リスト(℃)
Android 詳細
詳細画面
Android 設定
設定(℃/℉)

SwiftUIを知っていれば、Composeは「言語が違うだけでほぼ同じ感覚」で書けました。

③ Flutter版(Dart)の作り方

Flutterは 1つのコードで iOS と Android の両方が動くのが最大の特徴。リストは ListViewListTile を並べるだけです。

ここで知っておきたいのが——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)),
    ),
])
Flutter Android リスト
Androidで実行
Flutter 詳細
詳細画面
Flutter iOS リスト
同じコードがiOSでも

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

見た目を並べて比較

同じ「都市リスト」を3環境で並べました。左からiOS / Android / Flutterです。

iOSの見た目
iOS(SwiftUI)
Androidの見た目
Android(Compose)
Flutterの見た目
Flutter(Material)

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

iOS 華氏
iOS:詳細が72°F
Android 華氏
Android:72°F等
Flutter 華氏
Flutter:72°F等

実機の動きも並べてみた

同じ「一覧 → 都市をタップ → 詳細 → 戻る」の操作を3環境で撮りました。左からiOS / Android / Flutterです(個別に撮影)。

iOS(SwiftUI)
Android(Compose)
Flutter(Material)

遷移アニメや操作の反応は、どの環境も実用上じゅうぶん滑らかでした。

制作の比較表

観点iOS(Swift)Android(Kotlin)Flutter(Dart)
UIの書き方SwiftUI(宣言的)Compose(宣言的)Widget(宣言的)
1コードで両OSiOSのみAndroidのみ◎ iOS/Android両方
見た目の方向性iOS純正Material純正全OSで統一(Material)
書きながら確認XcodeプレビューStudioプレビューホットリロードが高速
コード量(同じ画面)少なめ(1本で両OS)
向いている人iOSに集中Androidに集中両OSを手早く

シミュレータで「できること・できないこと」

記事のもう一つの主役。実機を用意しなくてもシミュレータ/エミュレータでどこまで検証できるかを、実際に試してまとめました。

⭕ できること(実際に確認)

項目iOSシミュレータAndroidエミュレータFlutter(各シミュ上)
UI・操作・画面遷移・プルリフレッシュ
画面回転(横向き)
ダークモード・言語/地域切替
位置情報の擬似設定✅(プラグイン経由)
通信環境の擬似化(低速・遅延)
ネット通信(サーバー経由)

例として画面回転も問題なし。横向きでもレイアウトが追従しました。

Androidエミュレータで横向き表示
エミュレータで横向きに回転(できることの一例)

❌ できない/実機が必要

項目理由iOSAndroidFlutter
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つから触ってみてください。

それでは、今回はここまで。最後までありがとうございました😊