本页介绍了使用样式以在整个代码库中实现一致性的最佳实践,以及我们在设计 API 时遵循的原则。
正确做法
请遵循以下最佳实践:
正确做法:使用样式实现视觉效果,使用修饰符实现行为
使用 Styles API 进行视觉配置(背景、内边距、边框),并为点击逻辑、手势检测或无障碍功能等行为保留修饰符。
正确做法:在设计系统中公开样式参数
对于您自己的自定义设计系统组件,您应在修饰符参数后公开 Style 对象。
@Composable fun GradientButton( modifier: Modifier = Modifier, // ✅ DO: for design system components, expose a style modifier to consumers to be able to customize the components style: Style = Style ) { // Consume the style }
正确做法:将基于视觉效果的参数替换为样式
考虑将可组合项上的参数替换为单个 Style 参数。
例如:
// Before @Composable fun OldButton(background: Color, fontColor: Color) { } // After // ✅ DO: Replace visual-based parameters with a style that includes same properties @Composable fun NewButton(style: Style = Style) { }
正确做法:优先使用样式实现动画
使用内置 animate 块进行基于状态的样式设置,并使用动画来提高性能,而不是使用修饰符。
正确做法:利用“后写优先”原则
利用 style 属性会覆盖而非堆叠这一事实。
使用此属性可替换默认组件边框或背景,而无需使用多个参数。
错误做法
不建议采用以下模式:
错误做法:使用样式实现互动逻辑
请勿尝试在样式中处理 onClick 或手势检测。样式仅限于基于状态的视觉配置,因此不应处理业务逻辑;相反,它们只应根据状态具有不同的视觉效果。
错误做法:将默认样式作为默认参数提供
样式参数应始终使用 style: Style = Style 进行声明:
@Composable fun BadButton( modifier: Modifier = Modifier, // ❌ DON'T set a default style here as a parameter style: Style = Style { background(Color.Red) } ) { }
如需添加“默认”参数,请将传入的参数样式与定义的默认样式合并:
@Composable fun GoodButton( modifier: Modifier = Modifier, // ✅ Do: always pass it as a Style, do not pass other defaults style: Style = Style ) { // ... val defaultStyle = Style { background(Color.Red) } // ✅ Do Combine defaults inside with incoming parameter Box(modifier = modifier.styleable(styleState, defaultStyle, style)) { // your logic } }
错误做法:向基于布局的可组合项提供样式参数
虽然您可以向任何可组合项提供样式,但基于布局的可组合项或屏幕级可组合项不应接受样式,因为从消费者的角度来看,不清楚样式在此级别上的作用。 样式是为组件设计的,不一定是为布局设计的。
错误做法:在 Composition 中创建样式
CompositionLocals 在定义样式时读取,而不是在使用样式时读取。当实际使用样式时,CompositionLocal 的状态可能已更改,从而导致样式不准确。
// DON'T - Create styles in Composition that access composition locals in this way - this will likely lead to issues when style is used / accessed, as it would not get updated when the value changes. @Composable fun containerStyle(): Style { val background = MaterialTheme.colorScheme.background val onBackground = MaterialTheme.colorScheme.onBackground return Style { background(background) contentColor(onBackground) } } // Do: Instead, Create StyleScope extension functions for your subsystems to access themed composition Locals val StyleScope.colors: JetsnackColors get() = JetsnackTheme.LocalJetsnackTheme.currentValue.colors val StyleScope.typography: androidx.compose.material3.Typography get() = JetsnackTheme.LocalJetsnackTheme.currentValue.typography val StyleScope.shapes: Shapes get() = JetsnackTheme.LocalJetsnackTheme.currentValue.shapes // Access CompositionLocals val button = Style { background(colors.brandSecondary) shape(shapes.small) }
正确做法:为子系统值更改创建一个样式
例如,如果要在深色模式和浅色模式之间切换,请查询现有主题值(通过 CompositionLocal)以动态更改 Style:
// Do: Use CompositionLocals or themed values to create a single style val buttonStyle = Style { background(colors.brandSecondary) shape(shapes.small) }
正确做法:当组件在主题定义之间存在根本差异时,替换整个样式
如果样式对象在主题级别上存在根本差异,您可以替换整个样式对象。
例如,如果您要创建一个应用,该应用针对每个产品/页面或产品/服务提供不同的主题,并且样式的许多属性都不同,那么在主题级别上替换整套样式是可以接受的。
// DO Switch out whole styles when many properties differ - if Product A and Product B are two white labelled apps that provide different Themes. val productBThemedButton = Style { shape(shapes.small) background(colors.brandSecondary) // other properties are fundamentally different } val productAThemedButton = Style { shape(shapes.large) background(colors.brand) // other properties are fundamentally different }