Compose Multiplatform 実践:CMPでComposeを使用したクロスプラットフォーム画面の実装
Compose Multiplatform (略称CMP)
昨日、共通のMaterial3 Themeを構築しました
今日はクロスプラットフォームアプリの画面を作成していきます
CMP
ではCompose
を使用してAndroidとiOSの画面を作成し
Compose UIは完全にcommonMain
内に実装します
つまりUI部分はすべて共有できます
さらに、AndroidでもUIフレームワークとしてCompose
の使用を全面的に推進しているため
すでにComposeに慣れている人にとっては非常に有利です
目錄
- Compose Multiplatform 實戰:放輕鬆點,初探CMP
- Compose Multiplatform 實戰:初戰,安裝CMP環境吧
- Compose Multiplatform 實戰:續戰,用Wizard創建CMP專案
- Compose Multiplatform 實戰:在Android、iOS模擬器上跑CMP專案
- Compose Multiplatform 實戰:CMP的專案結構理解與編譯配置
- Compose Multiplatform 實戰:CMP中跨平台Android、iOS程式碼的進入點
- Compose Multiplatform 實戰:在CMP的Compose中用Material Design3 Theme
- Compose Multiplatform 實戰:CMP用Compose實作跨平台畫面
- Compose Multiplatform 實戰:使用 expect 和 actual 實現跨平台程式碼
- Compose Multiplatform 實戰:CMP中實作Compose Navigation頁面切換
- Compose Multiplatform 實戰:CMP中透過StateFlow來管理UI狀態
- Compose Multiplatform 實戰:CMP中實作NavigationBar底部欄
- Compose Multiplatform 實戰:CMP中使用koin來依賴注入Dependency Injection
- Compose Multiplatform 實戰:CMP實作跨平台資料庫SqlDelight
- Compose Multiplatform 實戰:CMP中使用ROOM開發跨平台資料庫 & 疑難雜症
- まずは
CMP
でComposeを使用して基本的なHello World画面を作成する方法を見てみましょう
(不変の例であるHello world
XD)
Compose
は宣言的UI
を採用しているため
実装する関数の前に@Composable
を追加するだけで
Composeのコンポーネントになります
CMPのcommonMain
に以下を追加します
// in ~/commonMain/
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name !")
}
- プレビューを表示したい場合
別の関数を作成し、その前に@Preview
を追加するだけで
IDE上でComposeのプレビューを表示できます
// in ~/commonMain/
@Preview
@Composable
fun GreetingPreview() { Greeting("Compose") }
実際にIDEの右側に@Preview
の画面が表示されます
Modifierは
Compose
でコンポーネントを修飾・設定するためのツールです
Compose UIコンポーネントの動作や外観を変更するための様々な機能を提供します
Modifier
を入力して
展開してみると
様々なオプションが用意されており
UIの動作や外観を設定できます
例えば、backgroundcolor、align、height、width、onClick…など
非常に多くの機能があるので、興味があれば自分で確認してみてください:
- 昨日Themeを作成した場合
Material3 theme
を使用してコンポーネントの背景色を設定できます
// in ~/commonMain/
@OptIn(KoinExperimentalAPI::class)
@Composable
@Preview
fun App() {
//ElegantAccessComposeThemeを通じてMaterial 3テーマを設定
ElegantAccessComposeTheme {
Greeting("Compose")
}
}
そして、このようにTextの外側にColumn
を追加し
Modifier.background(color = MaterialTheme.colorScheme.background)
を使用します
// in ~/commonMain/
@Composable
fun Greeting(name: String) {
Column(
modifier = Modifier
.background(color = MaterialTheme.colorScheme.background)
) {
Text(text = "Hello $name !")
}
}
-
アプリ開発において、AndroidでもiOSでも
カスタムツールバー
が必要になることがよくあります
-
そこで再利用可能なトップバーを作成できます
//in ~/commonMain/ @OptIn(ExperimentalMaterial3Api::class) @Composable fun MainAppBar( modifier: Modifier = Modifier, config: MainAppBarConfig, elevation: Dp = 4.dp, containerColor: Color = MaterialTheme.colorScheme.primaryContainer ) { CenterAlignedTopAppBar( title = config.title, colors = TopAppBarDefaults.mediumTopAppBarColors( containerColor = containerColor ), modifier = modifier.shadow(elevation = elevation), navigationIcon = { config.navigationIcon() }, actions = { config.actionIcon?.invoke() } ) }
ここでの核心概念
:
- ComposeネイティブのTopAppBar:
CenterAlignedTopAppBar
を使用 - 異なる画面では異なるトップバーの内容が必要になることを考慮し
データクラスMainAppBarConfig
を別途作成
トップバーを使用する際に
TopAppBarを繰り返し書く必要がなく
MainAppBarConfig
のインスタンスを作成するだけでいい - よく使用される変数は外部に公開
設定できるようにしています
例:elevation
データクラス
MainAppBarConfig
の実装
よく調整される要素をカスタマイズできるようにします
タイトルの長さ、テキスト、スタイル、戻るアイコンなど
// in ~/commonMain/
data class MainAppBarConfig(
val marqueeNum: Int = 0,
val titleText: @Composable () -> String = { "" },
val title: @Composable () -> Unit = {
DefaultTitleText(titleText(), marqueeNum)
},
val navigationIcon: @Composable () -> Unit = {},
val actionIcon: @Composable (() -> Unit)? = null,
)
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun DefaultTitleText(titleText: String, marqueeNum: Int) {
Text(
modifier = Modifier.basicMarquee(marqueeNum),
text = titleText,
style = MaterialTheme.typography.titleMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = ExtendedTheme.colors.onAppBar
)
}
- ここでは
createSetting
関数を作成します
前に作成したMainAppBarConfig
を使用して
設定したい内容を入力します
// in ~/commonMain/
private fun createSettingConfig(
navController: NavController,
) = MainAppBarConfig(
titleText = { stringResource(Res.string.title_setting) },
navigationIcon = {
NavBackIcon(navController = navController)
},
)
注:戻るボタンの遷移機能を実装したい場合
遷移イベントを関数に渡す必要があるかもしれません
ただし、NavController
を使用するとより柔軟になります
NavController
はすべてのルートを管理できます
遷移が必要なときに定義済みの文字列を指定するだけでいいです
この部分の詳細は後の章で説明します
- 実際の使用 ```kotlin // in ~/commonMain/
@Composable fun SettingScreen(navController: NavController) { val config = createSettingConfig(navController)
Scaffold(
topBar = {
MainAppBar(config = config)
},
containerColor = MaterialTheme.colorScheme.surfaceVariant
) {...} } ```
- 上記の概念を利用して
簡単な設定画面を実装できます
// in ~/commonMain/
@Composable
fun SettingScreen(navController: NavController) {
val config = createSettingConfig(navController)
Scaffold(
topBar = {
MainAppBar(config = config)
},
containerColor = MaterialTheme.colorScheme.surfaceVariant
) { paddingValues ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
item {
Text(
"Choose Transfer Option",
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(start = 30.dp, top = 16.dp, end = 30.dp)
)
}
items(SettingOption.values()) { option ->
SettingOptionCard(
option = option,
onClick = {
navController.navigate(option.route) {
navController.graph.startDestinationRoute?.let {
popUpTo(it) {
saveState = true
}
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
}