この記事はNTTテクノクロス Advent Calendar 2025 シリーズ1の11日目です。
こんにちは、NTTテクノクロスの戸部@etctaroと申します。
普段は社内でモバイルアプリ開発関連の技術支援や社内向けのノウハウ記事執筆、社内研修講師活動、社内コミュニティ活動などを行なっています。
加えて、最近は生成AIに関する開発や研修講師などもしています。トレンドに乗っかるスタイルです。
毎年恒例のアドカレですが、今年もモバイルアプリ関連の記事を一本を書きました。
今回のお題は「Compose Multiplatform」です。
2023年にもCompose Multiplatformについて記事を書きましたが、そこからバージョンが上がり、
またiOS/Androidともに大きくUI/UXが変更となっていますので、その点の影響なども踏まえたものとしようと思います。
★ところで、記事の前に宣伝です。
当社の有志メンバーでは技術系同人誌即売会の技術書典にて書籍を出しております。
直近の技術書典19でも書籍を頒布しました。
私が携わっているのは下記。
ぜひお手にとっていただければと思います。
はじめに
- Compose Multiplatformは、一言で表すと、Kotlinを使い、Jetpack Composeと同じ書式で、さまざまなプラットフォームのアプリを作ることができる仕組みです。
- 2023年は私のアドカレで恒例のQiitaビューアを作りましたが、その中でもいくつか課題がありました。
- 今回はそこを変えられないか試行錯誤してみます。
対象読者
- Jetpack Composeについては知っているが、Compose Multiplatformについては知らない人
- クロスプラットフォームアプリ開発を色々と模索している人
- Kotlinが好きな人
読んで得られること
- Compose Multiplatformの現状がなんとなく理解できる。
- マイグレーションの際のポイントを知ることができる。
2023年に作成した完成品
- リスト画面
- FABをタップするとQiitaのAPIに対してリクエストを行いその結果として、タイトルとURLの一覧を取得する。
- 画面にタイトルとURLを一覧表示する。
- 一覧の各アイテムをタップすることができる。タップすると、そのURLをパラメータとして詳細画面に遷移する。
- ついでに、アクションボタンでリストの内容を空にすることもできる。
- 詳細画面
- リスト画面から受け取ったURLを使ってそのURLのWebページをWebViewで表示する。
- 今回表示するのは、対応するQiitaの記事。
- また、詳細画面からリスト画面に戻るナビゲーションボタンもAppBarに用意する。
- その他
- AndroidとiOSに対応する。
画面イメージは以下の通りです。
- リスト画面と詳細画面の遷移
※画面の選択に他意はありません。内容を見て印象に残ったので選びました。
- iOSにも対応
- サンプルコードはこちらのリポジトリにあります。
Compose Multiplatformの最新動向
基本的には公式の What's newを追いかけます。
とはいえボリュームが多いので、生成AIを活用しながら要約などすると効率的に確認できます。
最近の動きとしては以下のような点が挙げられます。
- Composeの最新のコンポーネント群に対応(ドラッグ&ドロップなど)
- Navigation3に対応(現在はまだBeta)
- AndroidのMaterial 3 Expressive themeに対応 (実験的)
- Hot Reloadに対応(実験的)
開発の効率化、UI/UXの最新対応化、Jetpackのライブラリ群の導入などという感じになります。
(とは書いてはみたものの、この辺りは常に更新されるものですかね。)
今回対応するテーマ
今回は以下2点に対応しました。
- Navigation
- ライブラリ類の最新化
今回の環境(執筆時の環境)
- Android Studio Narwhal feature dropにて開発。(都合により最新ではありません)
- Kotlin 2.2.21
- Compose 1.9.x
- Android 16 の実機/エミュレータ
- iOS 16.x のシミュレータ
- Xcode 15.2 (直接は使わないけどインストールと初回起動が必須です。)
Navigation
さて、2023年度の記事ではこんなことを書きました。
次に、2つの画面間の遷移について追加します。
現時点では公式のナビゲーションライブラリはありませんので、サードパーティのライブラリを使うことにします。
まずはここを手術しようと思います。
じつは、Compose Multiplatformの公式ではドキュメントの記載が変わっています。
Android's Navigation library supports navigation in Jetpack Compose. The Compose Multiplatform team contributes multiplatform support to the AndroidX Navigation library.
すなわち、Androidではお馴染み、Navigationライブラリが Compose Multiplatformでも使えます、と言うのが現状です。ノータイムで導入ですね。
kotlin {
// ...
sourceSets {
// ...
commonMain.dependencies {
// ...
implementation("org.jetbrains.androidx.navigation:navigation-compose:2.9.1")
}
// ...
}
}
- ライブラリ類の最新化
次の項でも記載しますが、navigationを導入するにあたって、ライブラリ類の更新も必要になりました。
後述します。
画面遷移実行部分
画面遷移の実装については、公式のNavigation のドキュメントがそのまま参考になります。
当初は2年間の記事で利用していたPreComposeを参考にしようとしましたが、Navigation自体も大きく変更が入っていましたので、そのままでは使えませんでした。
- 2年前の記事より
Column(modifier = Modifier.clickable {
navigator.navigate("/detail?url=${it.url}")
}) {
//...
- 今回の実装
@Serializable
object ArticleList
@Serializable
data class Detail(val url: String)
@Composable
fun App() {
MaterialTheme {
// Creates the NavController
val navController = rememberNavController()
// Creates the NavHost with the navigation graph consisting of supplied destinations
NavHost(navController = navController, startDestination = ArticleList) {
composable<ArticleList> { ListScreen(navController = navController) }
composable<Detail> { backStackEntry ->
val detail: Detail = backStackEntry.toRoute()
DetailScreen(
detail = detail,
navController
)
}
}
}
}
上記の通り、NavHostのComposableを使ってNavGraphを構成し、Navigatorを各Composableに渡し、navigateメソッドを呼ぶことで、他の画面に遷移できます。
また、各画面に渡すパラメータについても、Navigationのルールに基づいて実装しています。
各画面の情報はSerializableのアノテーションをつけてオブジェクトにしています。
2年前はNavHostの中でテキストベースを使い実装をしていましたが、オブジェクトを使った実装にすることで型安全になっています。
逆に、以前の書き方は使えなくなっているのでこの点はご注意ください。
- navigationを呼ぶ側の実装
このように、navControllerを渡し、navigateメソッドを呼びます。
navigateの引数はシリアライズしたDetailオブジェクトを使います。
@Composable
fun ListScreen(navController: NavHostController) {
// (省略)
LazyColumn {
items(resultList) { article ->
ListItem(
modifier = Modifier.clickable {
navController.navigate(route = Detail(url = article.url))
}
) {
Column {
Text(article.title, style = MaterialTheme.typography.body1)
}
}
Divider()
}
}
ポイント
- Navigationライブラリが使える
- Navigationを使うときは各画面の情報をオブジェクトにしておくことが基本
- あとは公式ドキュメントに従えば問題なくいける
ライブラリ類の最新化
実はNavigationライブラリを使おうとするとバージョンが合わないことに気づきます。
マイグレーションについては公式ドキュメントがありますので、そちらを参照して対応します。
少し間を空けたことによって、この間にさまざまな変更が入っていました。以下は一例です。
これらに対しての対応が必要となります。
- Kotlin2系の対応
- Android Studioの更新に合わせた対応
- build.gradle.ktsの書式変更
- パッケージ類の変更への対応
既存からはこれくらいの変更を入れました。
これに加えて、一部だけパッケージ類の追加が必要となります。
- Navigation
implementation("org.jetbrains.androidx.navigation:navigation-compose:2.9.1")
- Material icons
implementation("org.jetbrains.compose.material:material-icons-core:1.7.3")
盲点だったのですが、固定のアイコン類についてはパッケージが切り出され、利用する場合は追加が必要になりました。
その他
さて、上記の画面の通り、
Android16では画面のEdge to Edgeの対応が必須となります。
AndroidネイティブならScaffold配下で以下のように書くことで、とりあえずステータスバーが重なる心配がないのですが、今回はうまくいかず。
paddingValues ->
LazyColumn(modifier = Modifier.padding(paddingValues)) {
悩ましく思いつつ、時間の関係上ScaffoldのAppBarを非表示にしました。
これはこれで、Edge to Edgeの対応版に見え、悪くはない。
とはいえ、ここは後日追加記事の対応を書いてみようかと思います。
より良いやり方がありますので。
最新UIへの対応について
★今回こちらの対応はしてはいません。
今年はiOSとAndroidともにUI/UXの大幅な変更が入りました。
特にLiquid Glassについては大きな話題となっています。
Compose Multiplatformでは正式な対応はしていませんが、
対応のために使えそうな設定値があり、
それを利用してLiquid Glass風にすることが可能です。
以下のような記事で試している人がいますのでぜひチェックしてみてください。
ただし、UI/UXをプラットフォームごとにこだわることについては課題もあるものと思われます。
- 個別のUI実装を増やす:
- 規模が増えるほどクロスプラットフォームの旨みが減ることになる。個別実装をするのと変わらなくなる。
- それぞれのネイティブ実装に明るい有識者が必要。
- 諦めて独自のデザインシステムにする:
- ※Flutterはこの考え方(独自のレンダリングエンジンという点で。)
- ユーザ体験が損なわれる(プラットフォーム全体での統一感という点で)
ここまで試してみての所感
いいところ
- Composeで実装するそのままの感覚でマルチプラットフォーム対応アプリが実装可能
- 通信・非同期処理のロジックもなど含め大体のことはKotlinで実装できる。
- Navigationのように手が届いていなかったところに公式対応が入り始めた。
- ほぼ1つのコードで作ることができる。Composeに対する知見が活用できる。
- 知見が活用できるということは、生成AIを使うことにより効率的に実装できる可能性がある。
まだまだなところ、気をつけないといけないところ
- syncなどでのライブラリ取得、ビルドなどに時間がかかる。
- ライブラリ管理が煩雑になりがち。
- 大幅にバージョンが入ると泣きを見る。
- PreviewやLive EditなどComposeの便利機能がまだ使えない。
- 見た目がAndroid(MaterialUI)寄りになる。
- そして、ネイティブ側のUI仕様変更に引っ張られる。
- 世界観をそれぞれのプラットフォームに合わせるなら個別対応が必須である。
- デザインに対する考え方がAndroid/iOSで異なるため、公式で推奨されているデザインシステムを表現するには避けられない
おわりに
ということで、今年もComposeを使った記事を書いてきました。
一昨年にも書きましたが、Kotlinで全体的に色々と対応できる点は非常に強いです。
ただし、最新のUIへの対応はまだまだのようです。
この辺りの方針がクリアになるとより安心して実装を勧められそうな気がします。
それでは、皆様、良いComposeライフを。
令和TXこそこそ噂話
シリーズ1の12日目は@shimizu-fu さんによるAzureの話のようです。
明日もお楽しみに。





