
关于
KMP 项目的 Compose Multiplatform 和 Jetpack Compose 模式——状态管理、导航、主题、性能和平台特定 UI。
name: compose-multiplatform-patterns description: 用于 KMP 项目的 Compose Multiplatform 和 Jetpack Compose 模式 — 状态管理、导航、主题、性能和平台特定 UI。 origin: ECC
Compose Multiplatform 模式
使用 Compose Multiplatform 和 Jetpack Compose 在 Android、iOS、桌面和 Web 之间构建共享 UI 的模式。涵盖状态管理、导航、主题和性能。
何时激活
- 构建 Compose UI(Jetpack Compose 或 Compose Multiplatform)
- 使用 ViewModel 和 Compose 状态管理 UI 状态
- 在 KMP 或 Android 项目中实现导航
- 设计可复用的 Composable 和设计系统
- 优化重组和渲染性能
状态管理
ViewModel + 单一状态对象
使用单个数据类表示屏幕状态。将其作为 StateFlow 暴露并在 Compose 中收集:
data class ItemListState(
val items: List<Item> = emptyList(),
val isLoading: Boolean = false,
val error: String? = null,
val searchQuery: String = ""
)
class ItemListViewModel(
private val getItems: GetItemsUseCase
) : ViewModel() {
private val _state = MutableStateFlow(ItemListState())
val state: StateFlow<ItemListState> = _state.asStateFlow()
fun onSearch(query: String) {
_state.update { it.copy(searchQuery = query) }
loadItems(query)
}
private fun loadItems(query: String) {
viewModelScope.launch {
_state.update { it.copy(isLoading = true) }
getItems(query).fold(
onSuccess = { items -> _state.update { it.copy(items = items, isLoading = false) } },
onFailure = { e -> _state.update { it.copy(error = e.message, isLoading = false) } }
)
}
}
}
在 Compose 中收集状态
@Composable
fun ItemListScreen(viewModel: ItemListViewModel = koinViewModel()) {
val state by viewModel.state.collectAsStateWithLifecycle()
ItemListContent(
state = state,
onSearch = viewModel::onSearch
)
}
@Composable
private fun ItemListContent(
state: ItemListState,
onSearch: (String) -> Unit
) {
// 无状态 composable — 易于预览和测试
}
事件接收器模式
对于复杂屏幕,使用密封接口表示事件,而不是多个回调 lambda:
sealed interface ItemListEvent {
data class Search(val query: String) : ItemListEvent
data class Delete(val itemId: String) : ItemListEvent
data object Refresh : ItemListEvent
}
// 在 ViewModel 中
fun onEvent(event: ItemListEvent) {
when (event) {
is ItemListEvent.Search -> onSearch(event.query)
is ItemListEvent.Delete -> deleteItem(event.itemId)
is ItemListEvent.Refresh -> loadItems(_state.value.searchQuery)
}
}
// 在 Composable 中 — 单个 lambda 代替多个
ItemListContent(
state = state,
onEvent = viewModel::onEvent
)
导航
类型安全导航(Compose Navigation 2.8+)
将路由定义为 @Serializable 对象:
@Serializable data object HomeRoute
@Serializable data class DetailRoute(val id: String)
@Serializable data object SettingsRoute
@Composable
fun AppNavHost(navController: NavHostController = rememberNavController()) {
NavHost(navController, startDestination = HomeRoute) {
composable<HomeRoute> {
HomeScreen(onNavigateToDetail = { id -> navController.navigate(DetailRoute(id)) })
}
composable<DetailRoute> { backStackEntry ->
val route = backStackEntry.toRoute<DetailRoute>()
DetailScreen(id = route.id)
}
composable<SettingsRoute> { SettingsScreen() }
}
}
对话框和底部工作表导航
使用 dialog() 和覆盖层模式代替命令式的显示/隐藏:
NavHost(navController, startDestination = HomeRoute) {
composable<HomeRoute> { /* ... */ }
dialog<ConfirmDeleteRoute> { backStackEntry ->
val route = backStackEntry.toRoute<ConfirmDeleteRoute>()
ConfirmDeleteDialog(
itemId = route.itemId,
onConfirm = { navController.popBackStack() },
onDismiss = { navController.popBackStack() }
)
}
}
Composable 设计
基于插槽的 API
使用插槽参数设计 Composable 以提高灵活性:
@Composable
fun AppCard(
modifier: Modifier = Modifier,
header: @Composable () -> Unit = {},
content: @Composable ColumnScope.() -> Unit,
actions: @Composable RowScope.() -> Unit = {}
) {
Card(modifier = modifier) {
Column {
header()
Column(content = content)
Row(horizontalArrangement = Arrangement.End, content = actions)
}
}
}
Modifier 顺序
Modifier 顺序很重要 — 按以下顺序应用:
Text(
text = "Hello",
modifier = Modifier
.padding(16.dp) // 1. 布局(padding、size)
.clip(RoundedCornerShape(8.dp)) // 2. 形状
.background(Color.Blue) // 3. 背景
.clickable { } // 4. 交互
)
兼容工具
Claude CodeCursor
标签
移动端
