styles

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Limitations

局限性

  • Warn the user that this skill is EXPERIMENTAL and requires updating to alpha version of Compose and opting in to the Experimental APIs.
  • This skill only supports custom UI components and custom themes.
  • This skill does not support Material Design component Styles.
  • 提醒用户本技能处于实验阶段,需要升级到Compose的alpha版本并启用Experimental APIs。
  • 本技能仅支持自定义UI组件和自定义主题。
  • 本技能不支持Material Design组件样式。

Prerequisites

前置条件

1. Upgrade dependencies

1. 升级依赖

  • The project must use
    compileSdk
    version 37 or higher.
  • The project must use
    androidx.compose.foundation:foundation
    version
    1.12.0-alpha01
    or higher.
  • Alternatively, the project must use Compose BOM version
    2026.04.01
    or higher.
  • The API requires this exact package:
    import androidx.compose.foundation.style.Style
  • 项目必须使用
    compileSdk
    版本37或更高。
  • 项目必须使用
    androidx.compose.foundation:foundation
    版本
    1.12.0-alpha01
    或更高。
  • 或者,项目必须使用Compose BOM版本
    2026.04.01
    或更高。
  • 该API需要导入特定包:
    import androidx.compose.foundation.style.Style

2. Configure compiler options to enable experimental API

2. 配置编译器选项以启用实验性API

You must opt-in to the experimental API at the project level. Add the following block to your module's
build.gradle.kts
:
kotlin {
    compilerOptions {
        jvmTarget = JvmTarget.fromTarget("17")
        freeCompilerArgs.add("-opt-in=androidx.compose.foundation.style.ExperimentalFoundationStyleApi")
    }
}
你必须在项目层面启用实验性API。在模块的
build.gradle.kts
中添加以下代码块:
kotlin {
    compilerOptions {
        jvmTarget = JvmTarget.fromTarget("17")
        freeCompilerArgs.add("-opt-in=androidx.compose.foundation.style.ExperimentalFoundationStyleApi")
    }
}

Core workflows and guides

核心工作流与指南

Refer to the official documentation to complete specific development tasks:
  • Basic Style Usage: To set backgrounds, sizes, and alignments on a component, follow the Compose Styles Fundamentals Guide.
  • State and Transitions: To configure property changes for state shifts (like pressed or hovered), follow the Animations and State-Based Styling Guide.
  • Architecture Trade offs: To decide when to use a Style versus a standard Modifier, follow the Styles versus Modifiers Comparison.
  • Theme Level Integration: To connect style definitions with custom themes, follow Theming with Styles and Custom Themes in Compose.
请参考官方文档完成具体开发任务:
  • 基础Style用法:如需为组件设置背景、尺寸和对齐方式,请遵循《Compose Styles基础指南》(references/android/develop/ui/compose/styles/fundamentals.md)。
  • 状态与过渡:如需配置状态切换(如按压或悬停)时的属性变化,请遵循《动画与基于状态的样式指南》(references/android/develop/ui/compose/styles/state-animations.md)。
  • 架构权衡:如需决定何时使用Style而非标准Modifier,请遵循《Style与Modifier对比》(references/android/develop/ui/compose/styles/styles-vs-modifiers.md)。
  • 主题层面集成:如需将样式定义与自定义主题关联,请遵循《使用Styles进行主题化》(references/android/develop/ui/compose/styles/theming.md)和《Compose中的自定义主题》(references/android/develop/ui/compose/designsystems/custom.md)。

Step-by-Step Migration Workflow

分步迁移工作流

Step 1: Analyze theme structure

步骤1:分析主题结构

  1. Locate your central theme file (such as
    Theme.kt
    ).
  2. Identify design tokens. Note references for colors, typography, and shapes (for example,
    LocalColorScheme
    ,
    LocalTypography
    , or
    LocalShapes
    ).
  3. If the project lacks Jetpack Compose dependencies, stop. Instruct the user to migrate to Jetpack Compose first.
  4. If the project imports
    androidx.compose.material.MaterialTheme
    , recommend migrating to Material 3 before proceeding.
  1. 找到你的核心主题文件(如
    Theme.kt
    )。
  2. 识别设计标记。记录颜色、排版和形状的引用(例如
    LocalColorScheme
    LocalTypography
    LocalShapes
    )。
  3. 如果项目缺少Jetpack Compose依赖,请停止操作。指导用户先迁移到Jetpack Compose。
  4. 如果项目导入了
    androidx.compose.material.MaterialTheme
    ,建议先迁移到Material 3再继续。

Step 2: Establish
ComponentStyles

步骤2:创建
ComponentStyles

  1. Create a new file named
    ComponentStyles.kt
    in your theme directory.
  2. Define a top-level data class to hold your component styles, for example, the Jetsnack one is called
    JetsnackStyles
    :
    kotlin
    object ExampleComponentStyles {
        val customButtonStyle: Style = {
    
        }
        val customTextFieldStyle: Style = {
    
        }
    }
    <br />
  3. Expose this class through your custom theme with a static reference, don't use
    CompositionLocals
    here as it's not required.
    kotlin
    @Immutable
    class JetsnackTheme(
        // other Design system properties
    ) {
        companion object {
            val colors: CustomThemingWithStyles.JetsnackColors
                @Composable @ReadOnlyComposable
                get() = LocalJetsnackTheme.current.colors
            // ...
    
            // add helper static reference
            val styles: ComponentStyles = ComponentStyles
        }
    }
    <br />
  4. Provide extensions on
    StyleScope
    to reference theme tokens directly if they are exposed using
    CompositionLocals
    . For example:
    kotlin
    val StyleScope.colors: JetsnackColors
        get() = LocalJetsnackTheme.currentValue.colors
    
    val StyleScope.typography: androidx.compose.material3.Typography
        get() = LocalJetsnackTheme.currentValue.typography
    
    val StyleScope.shapes: Shapes
        get() = LocalJetsnackTheme.currentValue.shapes
    <br />
  1. 在主题目录中创建一个名为
    ComponentStyles.kt
    的新文件。
  2. 定义一个顶层数据类来存储你的组件样式,例如Jetsnack项目中的
    JetsnackStyles
    kotlin
    object ExampleComponentStyles {
        val customButtonStyle: Style = {
    
        }
        val customTextFieldStyle: Style = {
    
        }
    }
    <br />
  3. 通过自定义主题的静态引用暴露此类,此处无需使用
    CompositionLocals
    kotlin
    @Immutable
    class JetsnackTheme(
        // 其他设计系统属性
    ) {
        companion object {
            val colors: CustomThemingWithStyles.JetsnackColors
                @Composable @ReadOnlyComposable
                get() = LocalJetsnackTheme.current.colors
            // ...
    
            // 添加辅助静态引用
            val styles: ComponentStyles = ComponentStyles
        }
    }
    <br />
  4. 如果主题标记通过
    CompositionLocals
    暴露,请在
    StyleScope
    上提供扩展函数以直接引用这些标记。例如:
    kotlin
    val StyleScope.colors: JetsnackColors
        get() = LocalJetsnackTheme.currentValue.colors
    
    val StyleScope.typography: androidx.compose.material3.Typography
        get() = LocalJetsnackTheme.currentValue.typography
    
    val StyleScope.shapes: Shapes
        get() = LocalJetsnackTheme.currentValue.shapes
    <br />

Step 3: Migrate a component to Styles API

步骤3:将组件迁移到Styles API

For each custom component (for example,
CustomButton
), complete the following sequence:
  1. If you are able to run an Android emulator, locate an existing screenshot test for the component. If none exists, create one using the existing project testing framework. If no framework exists, use UI Automator or Espresso to create a screenshot test with minimum required setup. Run the test and take a baseline screenshot of the Component. ELSE proceed to the next step without a screenshot test.
  2. Remove individual styling parameters : Remove styling parameters such as
    backgroundColor
    ,
    shape
    ,
    textStyle
    , and
    contentPadding
    from the signature - anything that
    StyleScope
    supports.
  3. Add the style parameter : Add
    style: Style = Style
    to the function signature.
  4. Declare state tracking : If the component is interactable, create a
    MutableStyleState
    using the interaction source. Update state fields (such as
    isEnabled
    ) inside the Composable to track the state correctly.
  5. Apply styleable modifier : Replace specific layout modifiers on the root element with
    Modifier.styleable()
    .
  6. Move defaults to ComponentStyles : Move hardcoded values from the component definition to a dedicated
    Style
    instance in
    ComponentStyles.kt
    .
  7. Validate component: Compare the baseline screenshot image taken at the start with the rendered Compose Preview of the new composable. Ignore string content; focus on layout and styling. Iterate on the Compose code until visual parity is achieved. Once verified, write a Compose UI test for the new composable.
对于每个自定义组件(如
CustomButton
),完成以下步骤:
  1. 如果可以运行Android模拟器,找到该组件现有的截图测试。如果没有,请使用项目现有的测试框架创建一个。如果没有测试框架,请使用UI Automator或Espresso创建一个最简配置的截图测试。运行测试并获取组件的基准截图。否则,直接进入下一步,无需截图测试。
  2. 移除单独的样式参数:从函数签名中移除
    backgroundColor
    shape
    textStyle
    contentPadding
    等样式参数——所有
    StyleScope
    支持的参数都要移除。
  3. 添加style参数:在函数签名中添加
    style: Style = Style
  4. 声明状态跟踪:如果组件是可交互的,使用交互源创建
    MutableStyleState
    。在Composable内部更新状态字段(如
    isEnabled
    )以正确跟踪状态。
  5. 应用styleable修饰符:将根元素上的特定布局修饰符替换为
    Modifier.styleable()
  6. 将默认值迁移到ComponentStyles:将组件定义中的硬编码值移至
    ComponentStyles.kt
    中专门的
    Style
    实例。
  7. 验证组件:将开始时获取的基准截图与新Composable的预览渲染结果进行对比。忽略文本内容,重点关注布局和样式。迭代修改Compose代码直至视觉效果一致。验证通过后,为新Composable编写Compose UI测试。

Migration example

迁移示例

Before Migration:
kotlin
@Composable
fun CustomButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    backgroundColor: Color = JetsnackTheme.colors.brandLight,
    disabledBackgroundColor: Color = JetsnackTheme.colors.brandSecondary,
    shape: Shape = JetsnackTheme.shapes.extraLarge,
    textStyle: TextStyle = JetsnackTheme.typography.labelLarge,
    enabled: Boolean = true,
    content: @Composable RowScope.() -> Unit,
) {
    val interactionSource = remember { MutableInteractionSource() }
    Row(
        modifier
            .clickable(onClick = onClick, indication = null, interactionSource = interactionSource)
            .background(if (enabled) backgroundColor else disabledBackgroundColor, shape)
            .defaultMinSize(58.dp, 40.dp),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically,
        content = content,
    )
}
<br />
After Migration:
kotlin
// Exposed via ComponentStyles.kt
object ComponentStyles {
    val buttonStyle = Style {
        background(colors.brandLight)
        shape(shapes.extraLarge)
        minWidth(58.dp)
        minHeight(40.dp)
        textStyle(typography.labelLarge)
        disabled {
            background(colors.brandSecondary)
        }
    }
}

@Composable
fun CustomButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    style: Style = Style,
    enabled: Boolean = true,
    content: @Composable RowScope.() -> Unit,
) {
    val interactionSource = remember { MutableInteractionSource() }
    val styleState = rememberUpdatedStyleState(interactionSource) {
        it.isEnabled = enabled
    }
    Row(
        modifier
            .clickable(onClick = onClick, indication = null, interactionSource = interactionSource)
            .styleable(styleState, JetsnackTheme.styles.buttonStyle, style),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically,
        content = content,
    )
}
<br />
迁移前:
kotlin
@Composable
fun CustomButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    backgroundColor: Color = JetsnackTheme.colors.brandLight,
    disabledBackgroundColor: Color = JetsnackTheme.colors.brandSecondary,
    shape: Shape = JetsnackTheme.shapes.extraLarge,
    textStyle: TextStyle = JetsnackTheme.typography.labelLarge,
    enabled: Boolean = true,
    content: @Composable RowScope.() -> Unit,
) {
    val interactionSource = remember { MutableInteractionSource() }
    Row(
        modifier
            .clickable(onClick = onClick, indication = null, interactionSource = interactionSource)
            .background(if (enabled) backgroundColor else disabledBackgroundColor, shape)
            .defaultMinSize(58.dp, 40.dp),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically,
        content = content,
    )
}
<br />
迁移后:
kotlin
// 通过ComponentStyles.kt暴露
object ComponentStyles {
    val buttonStyle = Style {
        background(colors.brandLight)
        shape(shapes.extraLarge)
        minWidth(58.dp)
        minHeight(40.dp)
        textStyle(typography.labelLarge)
        disabled {
            background(colors.brandSecondary)
        }
    }
}

@Composable
fun CustomButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    style: Style = Style,
    enabled: Boolean = true,
    content: @Composable RowScope.() -> Unit,
) {
    val interactionSource = remember { MutableInteractionSource() }
    val styleState = rememberUpdatedStyleState(interactionSource) {
        it.isEnabled = enabled
    }
    Row(
        modifier
            .clickable(onClick = onClick, indication = null, interactionSource = interactionSource)
            .styleable(styleState, JetsnackTheme.styles.buttonStyle, style),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically,
        content = content,
    )
}
<br />

Step 4: Validate Changes

步骤4:验证变更

  1. Build the project. Verify that there are no compilation errors.
  2. Run your module's screenshot tests.
  3. Compare visual outputs of the whole app between the previous and updated components. Verify that no visual layout regressions occur.
  1. 构建项目。确保没有编译错误。
  2. 运行模块的截图测试。
  3. 对比更新前后整个应用的视觉输出。确保没有出现视觉布局回归问题。