Compose Multiplatform Patterns

affaan-m/ECC/docs/zh-CN/skills/compose-multiplatform-patterns

作者 affaan-mef648e01899ba3e8dc6371642deaaf64b4477775无许可证275K 个星标收录于 2026年10月9日更新于 2026年10月9日仓库4天前更新

KMP项目中的Compose Multiplatform和Jetpack Compose模式——状态管理、导航、主题化、性能优化和平台特定UI。

AI 生成的概览

用于在 Android、iOS、桌面和 Web 上构建共享 Compose Multiplatform 与 Jetpack Compose UI 的模式。

功能
提供 Compose Multiplatform 与 Jetpack Compose UI 开发的参考模式和代码示例,涵盖基于 ViewModel 与 StateFlow 的状态管理、类型安全导航、基于槽位的可组合项设计、主题化以及重组性能。还展示 KMP 的 expect/actual 平台特定可组合项,并列出应避免的反模式。它只是纯说明性参考,不产出文件或脚本。
适用场景
适用于在 Jetpack Compose 或 Compose Multiplatform 项目中构建 Compose UI、用 ViewModel 管理 UI 状态、实现导航、设计可复用可组合项或设计系统,以及优化重组与渲染性能的场景。
运行要求
不附带脚本或工具,仅为指导内容。应用其中的示例需要具备 Compose、Compose Navigation、ViewModel 以及协程/Flow 库的 Kotlin Multiplatform 或 Android 项目。

Compose 多平台模式

使用 Compose Multiplatform 和 Jetpack Compose 构建跨 Android、iOS、桌面和 Web 的共享 UI 的模式。涵盖状态管理、导航、主题和性能。

何时启用

  • 构建 Compose UI(Jetpack Compose 或 Compose Multiplatform)
  • 使用 ViewModel 和 Compose 状态管理 UI 状态
  • 在 KMP 或 Android 项目中实现导航
  • 设计可复用的可组合项和设计系统
  • 优化重组和渲染性能

状态管理

ViewModel + 单一状态对象

使用单个数据类表示屏幕状态。将其暴露为 StateFlow 并在 Compose 中收集:

kotlin
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 中收集状态

kotlin
@Composablefun ItemListScreen(viewModel: ItemListViewModel = koinViewModel()) {    val state by viewModel.state.collectAsStateWithLifecycle()
    ItemListContent(        state = state,        onSearch = viewModel::onSearch    )}
@Composableprivate fun ItemListContent(    state: ItemListState,    onSearch: (String) -> Unit) {    // Stateless composable — easy to preview and test}

事件接收器模式

对于复杂屏幕,使用密封接口表示事件,而非多个回调 lambda:

kotlin
sealed interface ItemListEvent {    data class Search(val query: String) : ItemListEvent    data class Delete(val itemId: String) : ItemListEvent    data object Refresh : ItemListEvent}
// In ViewModelfun onEvent(event: ItemListEvent) {    when (event) {        is ItemListEvent.Search -> onSearch(event.query)        is ItemListEvent.Delete -> deleteItem(event.itemId)        is ItemListEvent.Refresh -> loadItems(_state.value.searchQuery)    }}
// In Composable — single lambda instead of manyItemListContent(    state = state,    onEvent = viewModel::onEvent)

导航

类型安全导航(Compose Navigation 2.8+)

将路由定义为 @Serializable 对象:

kotlin
@Serializable data object HomeRoute@Serializable data class DetailRoute(val id: String)@Serializable data object SettingsRoute
@Composablefun 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() 和覆盖层模式,而非命令式的显示/隐藏:

kotlin
NavHost(navController, startDestination = HomeRoute) {    composable<HomeRoute> { /* ... */ }    dialog<ConfirmDeleteRoute> { backStackEntry ->        val route = backStackEntry.toRoute<ConfirmDeleteRoute>()        ConfirmDeleteDialog(            itemId = route.itemId,            onConfirm = { navController.popBackStack() },            onDismiss = { navController.popBackStack() }        )    }}

可组合项设计

基于槽位的 API

使用槽位参数设计可组合项以获得灵活性:

kotlin
@Composablefun 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)        }    }}

修饰符顺序

修饰符顺序很重要 —— 按此顺序应用:

kotlin
Text(    text = "Hello",    modifier = Modifier        .padding(16.dp)          // 1. Layout (padding, size)        .clip(RoundedCornerShape(8.dp))  // 2. Shape        .background(Color.White) // 3. Drawing (background, border)        .clickable { }           // 4. Interaction)

KMP 平台特定 UI

平台可组合项的 expect/actual

kotlin
// commonMain@Composableexpect fun PlatformStatusBar(darkIcons: Boolean)
// androidMain@Composableactual fun PlatformStatusBar(darkIcons: Boolean) {    val systemUiController = rememberSystemUiController()    SideEffect { systemUiController.setStatusBarColor(Color.Transparent, darkIcons) }}
// iosMain@Composableactual fun PlatformStatusBar(darkIcons: Boolean) {    // iOS handles this via UIKit interop or Info.plist}

性能

用于可跳过重组的稳定类型

当所有属性都稳定时,将类标记为 @Stable 或 @Immutable:

kotlin
@Immutabledata class ItemUiModel(    val id: String,    val title: String,    val description: String,    val progress: Float)

正确使用 key() 和惰性列表

kotlin
LazyColumn {    items(        items = items,        key = { it.id }  // Stable keys enable item reuse and animations    ) { item ->        ItemRow(item = item)    }}

使用 derivedStateOf 延迟读取

kotlin
val listState = rememberLazyListState()val showScrollToTop by remember {    derivedStateOf { listState.firstVisibleItemIndex > 5 }}

避免在重组中分配内存

kotlin
// BAD — new lambda and list every recompositionitems.filter { it.isActive }.forEach { ActiveItem(it, onClick = { handle(it) }) }
// GOOD — key each item so callbacks stay attached to the right rowval activeItems = remember(items) { items.filter { it.isActive } }activeItems.forEach { item ->    key(item.id) {        ActiveItem(item, onClick = { handle(item) })    }}

主题

Material 3 动态主题

kotlin
@Composablefun AppTheme(    darkTheme: Boolean = isSystemInDarkTheme(),    dynamicColor: Boolean = true,    content: @Composable () -> Unit) {    val colorScheme = when {        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {            if (darkTheme) dynamicDarkColorScheme(LocalContext.current)            else dynamicLightColorScheme(LocalContext.current)        }        darkTheme -> darkColorScheme()        else -> lightColorScheme()    }
    MaterialTheme(colorScheme = colorScheme, content = content)}

应避免的反模式

  • 在 ViewModel 中使用 mutableStateOf,而 MutableStateFlow 配合 collectAsStateWithLifecycle 对生命周期更安全
  • 将 NavController 深入传递到可组合项中 —— 应传递 lambda 回调
  • 在 @Composable 函数中进行繁重计算 —— 应移至 ViewModel 或 remember {}
  • 使用 LaunchedEffect(Unit) 作为 ViewModel 初始化的替代 —— 在某些设置中,它会在配置更改时重新运行
  • 在可组合项参数中创建新的对象实例 —— 会导致不必要的重组

参考资料

查看技能:android-clean-architecture 了解模块结构和分层。 查看技能:kotlin-coroutines-flows 了解协程和 Flow 模式。

来源与署名

来源:affaan-m/ECC位于docs/zh-CN/skills/compose-multiplatform-patterns提交ef648e0

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架