Compose Multiplatform で画面遷移をおこなう

2024.06.10

この記事では、Compose Multiplatform(以下、CMP)を使用して、画面遷移する方法を紹介する。例として起動画面からホーム画面へ画面遷移をおこなう。

Compose Multiplatformとは

CMPは、複数のプラットフォームに対応したUIを構築する宣言的フレームワークだ。KotlinとComposeを使用してAndroidとiOSで同じUIを共有するアプリを開発できる。プラットフォーム間でのコードの再利用性が高くなり、開発効率が大幅に向上することを期待している。

CMPプロジェクトを作成する方法、AndroidエミュレータとiOSシミュレータとで実行する方法を以下の記事で紹介している。

開発環境

  • MacBook Pro (16インチ, 2021)
  • macOS Sonoma 14.5
  • Xcode 15.4
  • Android Studio Jellyfish | 2023.3.1 Patch 1
  • Compose Plugin 1.6.10

Compose Multiplatform で画面遷移をおこなう

CMPを使用することで、同じコードベースでAndroidとiOSの両方に対応したアプリを開発できる。ここでは、ナビゲーションライブラリを使用して、起動画面からホーム画面への遷移を実装する手順を紹介する。

1. Navigation依存関係の追加

まず、Gradle バージョン カタログ(gradle/libs.versions.tomlファイル)を開き、Navigation Componentの利用するバージョンとライブラリの定義を追加する。

[versions]
androidx-navigation = "2.8.0-alpha02"
...

[libraries]
androidx-navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "androidx-navigation" }

次に build.gradle.ktsファイルの commonMain.dependencies に依存関係を追加する。

kotlin {
    sourceSets {
        commonMain.dependencies {
//...
            implementation(libs.androidx.navigation.compose) // <--- Add
        }
    }
}

2. NavHostの設定

次に、NavHostを使ってナビゲーションを設定する。AppNavHost を作成し、ナビゲーションの開始地点を指定している。

import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

@Composable
fun AppNavHost(
    navController: NavHostController = rememberNavController(),
) {
    NavHost(
        navController = navController,
        startDestination = "launch",
    ) {
        composable("launch") {
            LaunchScreen(navController)
        }
        composable("home") { HomeScreen() }
    }
}

元々定義されている App を削除し、新しく AppNavHost を表示するように変更する。

import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import org.jetbrains.compose.ui.tooling.preview.Preview

@Composable
@Preview
fun App() {
    MaterialTheme {
        AppNavHost()
    }
}

3. 起動画面とホーム画面を追加する

起動画面では CircularProgressIndicator を使って、ぐるぐる回るインジケータを表示させる。ここでは紹介しないが、この画面では、強制アップデートの可否を確認する処理や、マスタデータのダウンロードなどの処理を想定している。

@Composable
fun LaunchScreen() {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        CircularProgressIndicator()
    }
}

ホーム画面では、ホーム画面であることを示す Text だけを表示する。

@Composable
fun HomeScreen() {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(text = "Welcome to Home Screen")
    }
}

4. LaunchScreenからHomeScreenへの遷移

起動画面が表示されたときに2秒間遅延させ、その後にホーム画面に遷移させる。画面遷移時の処理をおこなうのにLaunchedEffectを使っている。

@Composable
fun LaunchScreen(
    navController: NavHostController
) {
    LaunchedEffect(Unit) {
        delay(2000) // 2秒待つ
        navController.navigate("home")
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        CircularProgressIndicator()
    }
}

アプリを実行する

最後に、起動画面からホーム画面へ画面遷移できるか確認する。

Androidエミュレータでアプリを実行する

Android エミュレータ上で起動画面からホーム画面への遷移を確認できた。

iOSシミュレータでアプリを実行する

iOSシミュレータ上で起動画面からホーム画面への遷移を確認できた。

以上で、iOS/Androidともに、同一のコードから画面遷移を実現することが確認できた。

(2024/06/17追記) CMPでプラットフォーム固有のコンポーネントを表示する

ひとつのコードベースからiOSとAndroidで同じUIを実装できるのがCompose Multiplatformの利点ではあるが、UIについてはそれぞれのプラットフォームでの世界観が存在している。たとえば今回、ぐるぐる回るインジケータをCircularProgressIndicatorで実装したが、iOSでは対応するUIコンポーネントは UIActivityIndicatorView になる。

expectactual宣言を利用して、各プラットフォームで表示するUIコンポーネントを使い分けることができる。本質的なものではないため、こだわるのであればあくまでもアプリが完成してからが良いと思うが、各プラットフォームでの世界観を守るために細部に手を入れると良いだろう。