compose-expert
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCompose Multiplatform Expert
Compose Multiplatform 实战指南
Visual UI patterns for sharing composables across Android and Desktop.
适用于在Android和Desktop之间共享可组合项的可视化UI模式。
When to Use This Skill
何时使用本技能
- Creating or refactoring shared UI components
- Deciding whether to share UI in or keep platform-specific
commonMain - Building custom ImageVector icons (robohash pattern)
- State management: remember, derivedStateOf, produceState
- Recomposition optimization: visual usage of @Stable/@Immutable
- Material3 theming and styling
- Performance: lazy lists, image loading
Delegate to other skills:
- Navigation structure → ,
android-expertdesktop-expert - Kotlin state patterns (StateFlow, sealed classes) →
kotlin-expert - Build configuration →
gradle-expert
- 创建或重构共享UI组件
- 决定是在中共享UI还是保留平台特定实现
commonMain - 构建自定义ImageVector图标(robohash模式)
- 状态管理:remember、derivedStateOf、produceState
- 重组优化:@Stable/@Immutable的可视化用法
- Material3主题与样式设计
- 性能优化:懒加载列表、图片加载
委托给其他技能:
- 导航结构 → 、
android-expertdesktop-expert - Kotlin状态模式(StateFlow、密封类) →
kotlin-expert - 构建配置 →
gradle-expert
Philosophy: Share by Default
设计理念:默认共享
Default to unless platform experts indicate otherwise.
commons/commonMain默认优先使用,除非平台专家另有指示。
commons/commonMainAlways Share
始终共享
- UI components: Buttons, cards, lists, dialogs, inputs
- State visualization: Loading, empty, error states
- Custom icons: ImageVector assets (robohash, custom paths)
- Theme utilities: Color calculations, style helpers
- Material3 components: Any UI using Material primitives
- UI组件:按钮、卡片、列表、对话框、输入框
- 状态可视化:加载、空数据、错误状态
- 自定义图标:ImageVector资源(robohash、自定义路径)
- 主题工具:颜色计算、样式辅助工具
- Material3组件:所有使用Material原语的UI
Keep Platform-Specific
保留平台特定实现
- Navigation structure: Bottom nav (Android) vs Sidebar (Desktop)
- Screen layouts: Platform-specific scaffolding
- System integrations: File pickers, notifications, share sheets
- Platform UX: Gestures, keyboard shortcuts, window management
- 导航结构:底部导航(Android) vs 侧边栏(Desktop)
- 屏幕布局:平台特定的脚手架
- 系统集成:文件选择器、通知、分享面板
- 平台UX:手势、键盘快捷键、窗口管理
Decision Framework
决策框架
- Uses only Material3 primitives? → Share in
commonMain - Requires platform system APIs? → Platform-specific
- Pure visual component without navigation? → Share in
commonMain - Needs platform UX patterns? → Ask or
android-expertdesktop-expert
If uncertain, default to sharing - easier to split later than merge.
- 是否仅使用Material3原语? → 在中共享
commonMain - 是否需要平台系统API? → 保留平台特定实现
- 是否为无导航的纯可视化组件? → 在中共享
commonMain - 是否需要平台UX模式? → 咨询或
android-expertdesktop-expert
若不确定,默认选择共享——后续拆分比合并更容易。
Shared Composable Anatomy
共享可组合项结构
Structure
结构示例
kotlin
@Composable
fun SharedComponent(
// State parameters (read-only)
data: DataClass,
isLoading: Boolean,
// Event parameters (write-only)
onAction: () -> Unit,
// Visual parameters
modifier: Modifier = Modifier,
// Optional customization
colors: ComponentColors = ComponentDefaults.colors()
) {
// Implementation
}Pattern: State down, events up
- Parameters above modifier = required state/events
- parameter = layout control
modifier - Parameters below modifier = optional customization
kotlin
@Composable
fun SharedComponent(
// 状态参数(只读)
data: DataClass,
isLoading: Boolean,
// 事件参数(只写)
onAction: () -> Unit,
// 可视化参数
modifier: Modifier = Modifier,
// 可选自定义配置
colors: ComponentColors = ComponentDefaults.colors()
) {
// 实现代码
}模式:状态向下传递,事件向上传递
- modifier之前的参数:必填状态/事件
- 参数:布局控制
modifier - modifier之后的参数:可选自定义配置
Example: AddButton
示例:AddButton
kotlin
@Composable
fun AddButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
text: String = "Add",
enabled: Boolean = true
) {
OutlinedButton(
modifier = modifier,
enabled = enabled,
onClick = onClick,
shape = ActionButtonShape,
contentPadding = ActionButtonPadding
) {
Text(text = text, textAlign = TextAlign.Center)
}
}
// Shared constants for consistency
val ActionButtonShape = RoundedCornerShape(20.dp)
val ActionButtonPadding = PaddingValues(vertical = 0.dp, horizontal = 16.dp)Why this works on all platforms:
- Material3 primitives (OutlinedButton, Text)
- No platform APIs
- Configurable through parameters
- Consistent styling via shared constants
kotlin
@Composable
fun AddButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
text: String = "Add",
enabled: Boolean = true
) {
OutlinedButton(
modifier = modifier,
enabled = enabled,
onClick = onClick,
shape = ActionButtonShape,
contentPadding = ActionButtonPadding
) {
Text(text = text, textAlign = TextAlign.Center)
}
}
// 用于保持一致性的共享常量
val ActionButtonShape = RoundedCornerShape(20.dp)
val ActionButtonPadding = PaddingValues(vertical = 0.dp, horizontal = 16.dp)为何能在全平台生效:
- 使用Material3原语(OutlinedButton、Text)
- 未调用平台API
- 通过参数实现可配置
- 通过共享常量保持样式一致
State Management Patterns
状态管理模式
remember - Cache Across Recompositions
remember - 在重组间缓存状态
kotlin
@Composable
fun ExpandableCard() {
var isExpanded by remember { mutableStateOf(false) }
Column {
IconButton(onClick = { isExpanded = !isExpanded }) {
Icon(
if (isExpanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
contentDescription = if (isExpanded) "Collapse" else "Expand"
)
}
if (isExpanded) {
Text("Expanded content...")
}
}
}Visual pattern: Toggle button → state changes → UI expands/collapses
Use for: Simple UI state (toggles, counters, text input)
kotlin
@Composable
fun ExpandableCard() {
var isExpanded by remember { mutableStateOf(false) }
Column {
IconButton(onClick = { isExpanded = !isExpanded }) {
Icon(
if (isExpanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
contentDescription = if (isExpanded) "Collapse" else "Expand"
)
}
if (isExpanded) {
Text("Expanded content...")
}
}
}可视化模式:切换按钮 → 状态变更 → UI展开/收起
适用场景:简单UI状态(开关、计数器、文本输入)
derivedStateOf - Optimize Frequent Changes
derivedStateOf - 优化频繁变更
kotlin
@Composable
fun ScrollToTopButton(listState: LazyListState) {
// Only recomposes when showButton changes, not every scroll pixel
val showButton by remember {
derivedStateOf {
listState.firstVisibleItemIndex > 0
}
}
if (showButton) {
FloatingActionButton(onClick = { /* scroll to top */ }) {
Icon(Icons.Default.ArrowUpward, null)
}
}
}Visual pattern: Scroll position (0, 1, 2...) → boolean (show/hide) → Button visibility
Use for: Input changes frequently, derived result changes rarely
Performance: Prevents recomposition on every scroll event
kotlin
@Composable
fun ScrollToTopButton(listState: LazyListState) {
// 仅在showButton变化时重组,而非每次滚动像素变化
val showButton by remember {
derivedStateOf {
listState.firstVisibleItemIndex > 0
}
}
if (showButton) {
FloatingActionButton(onClick = { /* scroll to top */ }) {
Icon(Icons.Default.ArrowUpward, null)
}
}
}可视化模式:滚动位置(0、1、2...) → 布尔值(显示/隐藏) → 按钮可见性
适用场景:输入频繁变化,但派生结果很少变化的情况
性能优势:避免每次滚动事件都触发重组
produceState - Async to Compose State
produceState - 异步转Compose状态
kotlin
@Composable
fun LoadUserProfile(userId: String): State<User?> {
return produceState<User?>(initialValue = null, userId) {
value = repository.fetchUser(userId)
}
}
@Composable
fun ProfileScreen(userId: String) {
val user by LoadUserProfile(userId)
when (user) {
null -> LoadingState("Loading profile...")
else -> ProfileCard(user!!)
}
}Visual pattern: Async operation → state updates → UI reflects changes
Use for: Convert Flow, LiveData, callbacks into Compose state
Lifecycle: Coroutine cancelled when composable leaves composition
For Kotlin-specific state patterns (StateFlow, sealed classes), see .
kotlin-expertkotlin
@Composable
fun LoadUserProfile(userId: String): State<User?> {
return produceState<User?>(initialValue = null, userId) {
value = repository.fetchUser(userId)
}
}
@Composable
fun ProfileScreen(userId: String) {
val user by LoadUserProfile(userId)
when (user) {
null -> LoadingState("Loading profile...")
else -> ProfileCard(user!!)
}
}可视化模式:异步操作 → 状态更新 → UI反映变更
适用场景:将Flow、LiveData、回调转换为Compose状态
生命周期:当可组合项离开组合时,协程会被取消
关于Kotlin特定的状态模式(StateFlow、密封类),请参考。
kotlin-expertState Hoisting
状态提升
Move state up to make composables reusable:
kotlin
// ❌ Stateful - hard to test, can't control externally
@Composable
fun BadSearchBar() {
var query by remember { mutableStateOf("") }
TextField(value = query, onValueChange = { query = it })
}
// ✅ Stateless - reusable, testable
@Composable
fun GoodSearchBar(
query: String,
onQueryChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
TextField(
value = query,
onValueChange = onQueryChange,
modifier = modifier
)
}
@Composable
fun SearchScreen() {
var query by remember { mutableStateOf("") }
Column {
GoodSearchBar(query = query, onQueryChange = { query = it })
SearchResults(query = query)
}
}Principle: State up, events down
- State: (read-only parameter)
query: String - Events: (callback parameter)
onQueryChange: (String) -> Unit
将状态上移,使可组合项更具复用性:
kotlin
// ❌ 有状态 - 难以测试,无法外部控制
@Composable
fun BadSearchBar() {
var query by remember { mutableStateOf("") }
TextField(value = query, onValueChange = { query = it })
}
// ✅ 无状态 - 可复用、可测试
@Composable
fun GoodSearchBar(
query: String,
onQueryChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
TextField(
value = query,
onValueChange = onQueryChange,
modifier = modifier
)
}
@Composable
fun SearchScreen() {
var query by remember { mutableStateOf("") }
Column {
GoodSearchBar(query = query, onQueryChange = { query = it })
SearchResults(query = query)
}
}原则:状态向上传递,事件向上传递
- 状态:(只读参数)
query: String - 事件:(回调参数)
onQueryChange: (String) -> Unit
Recomposition Optimization
重组优化
Visual Usage of @Immutable
@Immutable的可视化用法
Use @Immutable on data classes passed to composables:
kotlin
@Immutable
data class UserProfile(val name: String, val avatar: String)
@Composable
fun ProfileCard(profile: UserProfile) {
// Only recomposes when profile instance changes
Row {
RobohashImage(robot = profile.avatar)
Text(profile.name, style = MaterialTheme.typography.titleMedium)
}
}Visual effect: Prevents recomposition when parent recomposes with same data
Pattern: Mark parameter data classes as @Immutable
Note: For Kotlin language details on @Immutable, see
kotlin-expert在传递给可组合项的数据类上使用@Immutable:
kotlin
@Immutable
data class UserProfile(val name: String, val avatar: String)
@Composable
fun ProfileCard(profile: UserProfile) {
// 仅当profile实例变化时才会重组
Row {
RobohashImage(robot = profile.avatar)
Text(profile.name, style = MaterialTheme.typography.titleMedium)
}
}可视化效果:当父组件使用相同数据重组时,避免当前组件重组
模式:将参数数据类标记为@Immutable
注意:关于@Immutable的Kotlin语言细节,请参考
kotlin-expertStable Parameters
稳定参数
kotlin
// ✅ Stable - won't trigger recomposition unless colors instance changes
@Composable
fun ThemedCard(
content: String,
colors: CardColors = CardDefaults.colors(),
modifier: Modifier = Modifier
) {
Card(colors = colors, modifier = modifier) {
Text(content)
}
}For @Stable annotation details, see .
kotlin-expertkotlin
// ✅ 稳定 - 除非colors实例变化,否则不会触发重组
@Composable
fun ThemedCard(
content: String,
colors: CardColors = CardDefaults.colors(),
modifier: Modifier = Modifier
) {
Card(colors = colors, modifier = modifier) {
Text(content)
}
}关于@Stable注解的细节,请参考。
kotlin-expertMaterial3 Theming
Material3 主题
All shared composables use Material3 for consistency:
kotlin
@Composable
fun ThemedComponent() {
val bg = MaterialTheme.colorScheme.background
val fg = MaterialTheme.colorScheme.onBackground
val primary = MaterialTheme.colorScheme.primary
Column(
modifier = Modifier.background(bg)
) {
Text(
"Title",
style = MaterialTheme.typography.headlineMedium,
color = fg
)
Button(
onClick = { /* ... */ },
colors = ButtonDefaults.buttonColors(containerColor = primary)
) {
Text("Action")
}
}
}Principles:
- Colors:
MaterialTheme.colorScheme.* - Typography:
MaterialTheme.typography.* - Shapes:
MaterialTheme.shapes.*
所有共享可组合项均使用Material3以保持一致性:
kotlin
@Composable
fun ThemedComponent() {
val bg = MaterialTheme.colorScheme.background
val fg = MaterialTheme.colorScheme.onBackground
val primary = MaterialTheme.colorScheme.primary
Column(
modifier = Modifier.background(bg)
) {
Text(
"Title",
style = MaterialTheme.typography.headlineMedium,
color = fg
)
Button(
onClick = { /* ... */ },
colors = ButtonDefaults.buttonColors(containerColor = primary)
) {
Text("Action")
}
}
}设计原则:
- 颜色:
MaterialTheme.colorScheme.* - 排版:
MaterialTheme.typography.* - 形状:
MaterialTheme.shapes.*
Theme Detection
主题检测
kotlin
@Composable
private fun isLightTheme(): Boolean {
val background = MaterialTheme.colorScheme.background
return (background.red + background.green + background.blue) / 3 > 0.5f
}
@Composable
fun ThemedIcon() {
val isDark = !isLightTheme()
val tint = if (isDark) Color.White else Color.Black
Icon(Icons.Default.Face, null, tint = tint)
}kotlin
@Composable
private fun isLightTheme(): Boolean {
val background = MaterialTheme.colorScheme.background
return (background.red + background.green + background.blue) / 3 > 0.5f
}
@Composable
fun ThemedIcon() {
val isDark = !isLightTheme()
val tint = if (isDark) Color.White else Color.Black
Icon(Icons.Default.Face, null, tint = tint)
}Custom Icons: ImageVector Pattern
自定义图标:ImageVector模式
Amethyst uses ImageVector for multiplatform icons.
Amethyst使用ImageVector实现多平台图标。
roboBuilder DSL
roboBuilder DSL
kotlin
fun roboBuilder(block: Builder.() -> Unit): ImageVector {
return ImageVector.Builder(
name = "Robohash",
defaultWidth = 300.dp,
defaultHeight = 300.dp,
viewportWidth = 300f,
viewportHeight = 300f
).apply(block).build()
}kotlin
fun roboBuilder(block: Builder.() -> Unit): ImageVector {
return ImageVector.Builder(
name = "Robohash",
defaultWidth = 300.dp,
defaultHeight = 300.dp,
viewportWidth = 300f,
viewportHeight = 300f
).apply(block).build()
}Building Icons
构建图标
kotlin
fun customIcon(fgColor: SolidColor, builder: Builder) {
builder.addPath(pathData1, fill = fgColor, stroke = Black, strokeLineWidth = 1.5f)
builder.addPath(pathData2, fill = Black, fillAlpha = 0.4f)
builder.addPath(pathData3, fill = Black, fillAlpha = 0.2f)
}
private val pathData1 = PathData {
moveTo(144.5f, 87.5f)
reflectiveCurveToRelative(-51.0f, 3.0f, -53.0f, 55.0f)
lineToRelative(16.0f, 16.0f)
close()
}
@Composable
fun CustomIcon() {
Image(
painter = rememberVectorPainter(
roboBuilder {
customIcon(SolidColor(Color.Blue), this)
}
),
contentDescription = "Custom icon"
)
}Why ImageVector?
- Pure Kotlin, no XML
- Works on Android, Desktop, iOS
- GPU-accelerated
- Type-safe
kotlin
fun customIcon(fgColor: SolidColor, builder: Builder) {
builder.addPath(pathData1, fill = fgColor, stroke = Black, strokeLineWidth = 1.5f)
builder.addPath(pathData2, fill = Black, fillAlpha = 0.4f)
builder.addPath(pathData3, fill = Black, fillAlpha = 0.2f)
}
private val pathData1 = PathData {
moveTo(144.5f, 87.5f)
reflectiveCurveToRelative(-51.0f, 3.0f, -53.0f, 55.0f)
lineToRelative(16.0f, 16.0f)
close()
}
@Composable
fun CustomIcon() {
Image(
painter = rememberVectorPainter(
roboBuilder {
customIcon(SolidColor(Color.Blue), this)
}
),
contentDescription = "Custom icon"
)
}为何选择ImageVector?
- 纯Kotlin实现,无需XML
- 支持Android、Desktop、iOS
- GPU加速
- 类型安全
Caching Pattern
缓存模式
kotlin
object CustomIcons {
private val cache = mutableMapOf<String, ImageVector>()
fun get(key: String): ImageVector {
return cache.getOrPut(key) {
buildIcon(key)
}
}
}
@Composable
fun CachedIcon(key: String) {
Image(imageVector = CustomIcons.get(key), contentDescription = null)
}For detailed icon patterns, see .
references/icon-assets.mdkotlin
object CustomIcons {
private val cache = mutableMapOf<String, ImageVector>()
fun get(key: String): ImageVector {
return cache.getOrPut(key) {
buildIcon(key)
}
}
}
@Composable
fun CachedIcon(key: String) {
Image(imageVector = CustomIcons.get(key), contentDescription = null)
}关于详细的图标模式,请参考。
references/icon-assets.mdCommon Visual Patterns
常见可视化模式
State Visualization
状态可视化
kotlin
@Composable
fun DataScreen(uiState: UiState) {
when (uiState) {
is UiState.Loading -> LoadingState("Loading...")
is UiState.Empty -> EmptyState(
title = "No data",
onRefresh = { /* refresh */ }
)
is UiState.Error -> ErrorState(
message = uiState.message,
onRetry = { /* retry */ }
)
is UiState.Success -> ContentList(uiState.items)
}
}Components (all in ):
commons/commonMain- - Progress indicator + message
LoadingState - - Empty message + optional refresh button
EmptyState - - Error message + optional retry button
ErrorState
kotlin
@Composable
fun DataScreen(uiState: UiState) {
when (uiState) {
is UiState.Loading -> LoadingState("Loading...")
is UiState.Empty -> EmptyState(
title = "No data",
onRefresh = { /* refresh */ }
)
is UiState.Error -> ErrorState(
message = uiState.message,
onRetry = { /* retry */ }
)
is UiState.Success -> ContentList(uiState.items)
}
}组件(均位于):
commons/commonMain- - 进度指示器 + 提示消息
LoadingState - - 空数据提示 + 可选刷新按钮
EmptyState - - 错误提示 + 可选重试按钮
ErrorState
Relay Status (Amethyst Pattern)
中继状态(Amethyst模式)
kotlin
@Composable
fun RelayStatusIndicator(connectedCount: Int) {
val statusColor = when {
connectedCount == 0 -> RelayStatusColors.Disconnected
connectedCount < 3 -> RelayStatusColors.Connecting
else -> RelayStatusColors.Connected
}
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Icon(
imageVector = if (connectedCount > 0) Icons.Default.Check else Icons.Default.Close,
tint = statusColor,
modifier = Modifier.size(16.dp)
)
Text(
"$connectedCount relay${if (connectedCount != 1) "s" else ""}",
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}Visual mapping:
- 0 relays → Red + X icon
- 1-2 relays → Yellow + Check icon
- 3+ relays → Green + Check icon
kotlin
@Composable
fun RelayStatusIndicator(connectedCount: Int) {
val statusColor = when {
connectedCount == 0 -> RelayStatusColors.Disconnected
connectedCount < 3 -> RelayStatusColors.Connecting
else -> RelayStatusColors.Connected
}
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Icon(
imageVector = if (connectedCount > 0) Icons.Default.Check else Icons.Default.Close,
tint = statusColor,
modifier = Modifier.size(16.dp)
)
Text(
"$connectedCount relay${if (connectedCount != 1) "s" else ""}",
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}视觉映射:
- 0个中继 → 红色 + X图标
- 1-2个中继 → 黄色 + 对勾图标
- 3个及以上中继 → 绿色 + 对勾图标
Placeholder Pattern
占位符模式
kotlin
@Composable
fun PlaceholderScreen(
title: String,
description: String,
modifier: Modifier = Modifier
) {
Column(modifier = modifier) {
Text(title, style = MaterialTheme.typography.headlineMedium)
Spacer(Modifier.height(16.dp))
Text(description, color = MaterialTheme.colorScheme.onSurfaceVariant)
}
}
// Specific implementations
@Composable
fun SearchPlaceholder() = PlaceholderScreen(
title = "Search",
description = "Search for users, notes, and hashtags."
)Pattern: Generic composable + specific wrappers with preset text
kotlin
@Composable
fun PlaceholderScreen(
title: String,
description: String,
modifier: Modifier = Modifier
) {
Column(modifier = modifier) {
Text(title, style = MaterialTheme.typography.headlineMedium)
Spacer(Modifier.height(16.dp))
Text(description, color = MaterialTheme.colorScheme.onSurfaceVariant)
}
}
// 特定实现
@Composable
fun SearchPlaceholder() = PlaceholderScreen(
title = "Search",
description = "Search for users, notes, and hashtags."
)模式:通用可组合项 + 预设文本的特定封装
Performance
性能优化
Avoid Unnecessary Recomposition
避免不必要的重组
kotlin
// ❌ Bad - recomposes on every scroll
@Composable
fun BadButton(scrollState: ScrollState) {
if (scrollState.value > 100) {
Button(onClick = {}) { Text("Top") }
}
}
// ✅ Good - only recomposes when visibility changes
@Composable
fun GoodButton(scrollState: ScrollState) {
val show by remember { derivedStateOf { scrollState.value > 100 } }
if (show) {
Button(onClick = {}) { Text("Top") }
}
}kotlin
// ❌ 不良实现 - 每次滚动都会重组
@Composable
fun BadButton(scrollState: ScrollState) {
if (scrollState.value > 100) {
Button(onClick = {}) { Text("Top") }
}
}
// ✅ 良好实现 - 仅在可见性变化时重组
@Composable
fun GoodButton(scrollState: ScrollState) {
val show by remember { derivedStateOf { scrollState.value > 100 } }
if (show) {
Button(onClick = {}) { Text("Top") }
}
}Lazy Lists
懒加载列表
kotlin
@Composable
fun FeedList(items: List<Item>) {
LazyColumn {
items(items, key = { it.id }) { item ->
FeedItem(item)
}
}
}Key principle: Use parameter for stable item identity
keykotlin
@Composable
fun FeedList(items: List<Item>) {
LazyColumn {
items(items, key = { it.id }) { item ->
FeedItem(item)
}
}
}核心原则:使用参数确保列表项的稳定标识
keyBundled Resources
捆绑资源
- references/shared-composables-catalog.md - Complete catalog of shared UI components
- references/state-patterns.md - State management patterns with visual examples
- references/icon-assets.md - Custom ImageVector icon patterns
- scripts/find-composables.sh - Find all @Composable functions in codebase
- references/shared-composables-catalog.md - 完整的共享UI组件目录
- references/state-patterns.md - 带可视化示例的状态管理模式
- references/icon-assets.md - 自定义ImageVector图标模式
- scripts/find-composables.sh - 在代码库中查找所有@Composable函数的脚本
Quick Reference
快速参考
| Task | Pattern | Location |
|---|---|---|
| Reusable UI | State hoisting | commons/commonMain |
| Simple state | remember { mutableStateOf() } | Composable scope |
| Derived state | derivedStateOf { } | remember block |
| Async → state | produceState { } | Composable function |
| Custom icons | roboBuilder + PathData | commons/icons |
| Loading/Error | LoadingState, ErrorState | commons/ui/components |
| Theme colors | MaterialTheme.colorScheme | Any @Composable |
| Navigation | Delegate to platform expert | amethyst/, desktopApp/ |
| 任务 | 模式 | 位置 |
|---|---|---|
| 可复用UI | 状态提升 | commons/commonMain |
| 简单状态 | remember { mutableStateOf() } | 可组合项作用域 |
| 派生状态 | derivedStateOf { } | remember代码块 |
| 异步转状态 | produceState { } | 可组合项函数 |
| 自定义图标 | roboBuilder + PathData | commons/icons |
| 加载/错误状态 | LoadingState、ErrorState | commons/ui/components |
| 主题颜色 | MaterialTheme.colorScheme | 任意@Composable |
| 导航 | 委托给平台专家 | amethyst/、desktopApp/ |
Common Workflows
常见工作流
Creating a Shared Component
创建共享组件
- Start in
commons/src/commonMain/kotlin/.../ui/components/ - Use Material3 primitives only
- Hoist state (parameters for data, callbacks for events)
- Add modifier parameter
- Use MaterialTheme for colors/typography
- Test on both Android and Desktop
- 从开始
commons/src/commonMain/kotlin/.../ui/components/ - 仅使用Material3原语
- 提升状态(数据使用参数,事件使用回调)
- 添加modifier参数
- 使用MaterialTheme获取颜色/排版
- 在Android和Desktop上进行测试
Converting Existing Component
转换现有组件
- Read current implementation in or
amethyst/desktopApp/ - Identify pure visual logic (no platform APIs)
- Create in with hoisted state
commons/commonMain - Replace platform implementations with shared component
- Keep platform-specific wrappers if needed
- 阅读或
amethyst/中的当前实现desktopApp/ - 识别纯可视化逻辑(无平台API)
- 在中创建带状态提升的版本
commons/commonMain - 使用共享组件替换平台特定实现
- 若需要,保留平台特定的封装
Custom Icon
创建自定义图标
- Export SVG from design tool
- Convert to PathData using Android Studio
- Create icon function with roboBuilder
- Add caching if generated dynamically
- Wrap in @Composable for easy use
- 从设计工具导出SVG
- 使用Android Studio转换为PathData
- 使用roboBuilder创建图标函数
- 若为动态生成的图标,添加缓存
- 封装为@Composable以便使用
Navigation (Delegate)
导航(委托)
For navigation patterns:
- Android bottom nav →
android-expert - Desktop sidebar →
desktop-expert - Multi-window →
desktop-expert
关于导航模式:
- Android底部导航 →
android-expert - Desktop侧边栏 →
desktop-expert - 多窗口 →
desktop-expert
Related Skills
相关技能
- kotlin-expert - Kotlin language aspects (@Immutable details, StateFlow, sealed classes)
- android-expert - Android navigation, platform APIs
- desktop-expert - Desktop navigation, window management, OS specifics
- kotlin-coroutines - Async patterns, Flow integration
- kotlin-expert - Kotlin语言层面内容(@Immutable细节、StateFlow、密封类)
- android-expert - Android导航、平台API
- desktop-expert - Desktop导航、窗口管理、系统特性
- kotlin-coroutines - 异步模式、Flow集成