Sugerencias y precauciones con los estilos

En esta página, se describen las prácticas recomendadas para trabajar con estilos que logren la coherencia en toda tu base de código, así como los principios que seguimos durante el diseño de las APIs.

Qué hacer

Sigue las prácticas recomendadas a continuación:

Usa estilos para elementos visuales y modificadores para comportamientos

Usa la API de Styles para la configuración visual (fondos, relleno, bordes) y reserva los modificadores para comportamientos como la lógica de clics, la detección de gestos o la accesibilidad.

Expón los parámetros de estilo en los sistemas de diseño

Para tus propios componentes personalizados del sistema de diseño, debes exponer un objeto Style después del parámetro modificador.

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

Reemplaza los parámetros basados en elementos visuales por un estilo

Considera reemplazar los parámetros en tus elementos componibles por un solo parámetro Style. Por ejemplo:

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

Prioriza los estilos para las animaciones

Usa el bloque animate integrado para el diseño basado en el estado con animaciones para obtener mejoras de rendimiento en los modificadores.

Aprovecha la función "Last-write-wins"

Aprovecha el hecho de que las propiedades style se sobrescriben en lugar de apilarse. Usa esto para anular los bordes o fondos de los componentes predeterminados sin necesidad de varios parámetros.

Qué no hacer

No se recomiendan los siguientes patrones:

No uses estilos para la lógica de interacción

No intentes controlar onClick ni la detección de gestos dentro de un estilo. Los estilos se limitan a las configuraciones visuales basadas en el estado, por lo que no deben controlar la lógica empresarial. En cambio, solo deben tener un elemento visual diferente según el estado.

No proporciones un estilo predeterminado como parámetro predeterminado

Los parámetros de estilo siempre se deben declarar con 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) }
) {
}

Para incluir un parámetro "predeterminado", combina el estilo del parámetro entrante con el valor predeterminado definido:

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

No proporciones parámetros de estilo a elementos componibles basados en el diseño

Aunque puedes proporcionar un estilo a cualquier elemento componible, no se espera que los elementos componibles basados en el diseño o los elementos componibles a nivel de la pantalla acepten el estilo. Desde el punto de vista del consumidor, no está claro qué haría un estilo en este nivel. Los estilos están diseñados para componentes, no necesariamente para diseños.

No crees estilos en Composition

Los CompositionLocals se leen en el punto en el que se define el estilo, no en el que se consume. Cuando se usa el estilo, el estado de CompositionLocal podría haber cambiado, lo que generaría un estilo impreciso.

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

Crea un estilo para los cambios de valor del subsistema

Por ejemplo, si cambias entre el modo oscuro y el claro, consulta los valores temáticos existentes (a través de CompositionLocal) para cambiar el Style de forma dinámica:

// Do: Use CompositionLocals or themed values to create a single style
val buttonStyle = Style {
    background(colors.brandSecondary)
    shape(shapes.small)
}

Cambia los estilos completos cuando el componente difiere fundamentalmente en las definiciones de temas

Puedes cambiar objetos de estilo completos a nivel del tema si son temas fundamentalmente diferentes.

Por ejemplo, si creas una app que tiene diferentes temas por producto o página, o bien oferta, y muchas propiedades de un estilo son diferentes, es aceptable cambiar conjuntos completos de estilos a nivel del tema.

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