affaan-m/ECC

compose-multiplatform-patterns

Use it for engineering and design tasks; the detail page covers purpose, installation, and practical steps.

61Collecting
See how to use itView GitHub source
npx skills add https://github.com/affaan-m/ECC --skill "docs/zh-CN/skills/compose-multiplatform-patterns"
Automated source guideDocumentationDeep source

Source checked Jul 28, 2026·Refresh due Oct 26, 2026

Reorganized from the pinned upstream SKILL.md

Source-grounded documentation guide: compose-multiplatform-patterns

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

npx skills add https://github.com/affaan-m/ECC --skill "docs/zh-CN/skills/compose-multiplatform-patterns"
Check the pinned source

The pinned source contains enough sections and task detail for a source-grounded deep guide; automated content is still not an independent test.

555 source words · 22 usable sections

Documentation workflow

Read compose-multiplatform-patterns through these 5 source sections

Sections are extracted automatically from the pinned SKILL.md and link back to the source.

01

何时启用

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

SKILL.md · 何时启用
构建 Compose UI(Jetpack Compose 或 Compose Multiplatform)使用 ViewModel 和 Compose 状态管理 UI 状态在 KMP 或 Android 项目中实现导航
02

状态管理

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

SKILL.md · 状态管理
使用单个数据类表示屏幕状态。将其暴露为 StateFlow 并在 Compose 中收集:对于复杂屏幕,使用密封接口表示事件,而非多个回调 lambda:
03

ViewModel + 单一状态对象

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

SKILL.md · ViewModel + 单一状态对象
使用单个数据类表示屏幕状态。将其暴露为 StateFlow 并在 Compose 中收集:
04

在 Compose 中收集状态

Review the “在 Compose 中收集状态” section in the pinned source before continuing.

SKILL.md · 在 Compose 中收集状态
Review and apply the “在 Compose 中收集状态” source section.
05

事件接收器模式

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

SKILL.md · 事件接收器模式
对于复杂屏幕,使用密封接口表示事件,而非多个回调 lambda:

SkillSignal prompt templates

Provide the task, context, and acceptance criteria

These prompts were written by SkillSignal from the source structure; they are not upstream text.

Source-grounded prompt

Use for a documentation task while explicitly checking the source sections.

Use compose-multiplatform-patterns for this documentation task: [task]. Inputs and constraints: [details]. Work through these pinned SKILL.md sections: “何时启用”, “状态管理”, “ViewModel + 单一状态对象”, “在 Compose 中收集状态”, “事件接收器模式”. Cite the concrete requirements that shape each step, do not invent capabilities absent from the source, and verify the result against: [acceptance criteria].

Documentation checklist

Verify each item before delivery

The source section “何时启用” has been checked.

The source section “状态管理” has been checked.

The source section “ViewModel + 单一状态对象” has been checked.

The source section “在 Compose 中收集状态” has been checked.

Choose a different workflow

When another Skill is the better fit

FAQ

What does the compose-multiplatform-patterns source document cover?

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

How do I install compose-multiplatform-patterns?

The source record exposes this install command: npx skills add https://github.com/affaan-m/ECC --skill "docs/zh-CN/skills/compose-multiplatform-patterns". Inspect the command and pinned source before running it.

Repository stars
234,327
Repository forks
35,711
Quality
61/100
Source repository last pushed

Quality breakdown

Based on traceable docs and repository signals; stars are not treated as quality.

61/100
Documentation22/30
Specificity7/25
Maintenance20/20
Trust signals12/25

Compare before choosing

Related Agent Skills and source variants

These links are selected from shared tasks, functions, stacks, platforms, and same-name variants. Compare the source owner, documentation, permissions, and maintenance signals.

View original Skill.mdThis page is parsed directly from the repository SKILL.md without editorial rewriting. Collected: Jul 28, 2026 · about 1 min

Compose 多平台模式

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

何时启用

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

状态管理

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
) {
    // Stateless composable — easy to preview and test
}

事件接收器模式

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

sealed interface ItemListEvent {
    data class Search(val query: String) : ItemListEvent
    data class Delete(val itemId: String) : ItemListEvent
    data object Refresh : ItemListEvent
}

// In 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)
    }
}

// In Composable — single lambda instead of many
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() }
        )
    }
}

可组合项设计

基于槽位的 API

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

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

修饰符顺序

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

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

// commonMain
@Composable
expect fun PlatformStatusBar(darkIcons: Boolean)

// androidMain
@Composable
actual fun PlatformStatusBar(darkIcons: Boolean) {
    val systemUiController = rememberSystemUiController()
    SideEffect { systemUiController.setStatusBarColor(Color.Transparent, darkIcons) }
}

// iosMain
@Composable
actual fun PlatformStatusBar(darkIcons: Boolean) {
    // iOS handles this via UIKit interop or Info.plist
}

性能

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

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

@Immutable
data class ItemUiModel(
    val id: String,
    val title: String,
    val description: String,
    val progress: Float
)

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

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

使用 derivedStateOf 延迟读取

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

避免在重组中分配内存

// BAD — new lambda and list every recomposition
items.filter { it.isActive }.forEach { ActiveItem(it, onClick = { handle(it) }) }

// GOOD — key each item so callbacks stay attached to the right row
val activeItems = remember(items) { items.filter { it.isActive } }
activeItems.forEach { item ->
    key(item.id) {
        ActiveItem(item, onClick = { handle(item) })
    }
}

主题

Material 3 动态主题

@Composable
fun 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 模式。

Source repo
affaan-m/ECC
Skill path
docs/zh-CN/skills/compose-multiplatform-patterns/SKILL.md
Commit SHA
4e973d3eaf92
Repository license
MIT
Data collected