O que fazer e o que não fazer com estilos

Esta página descreve as práticas recomendadas para trabalhar com estilos que alcançam consistência em toda a base de código, bem como os princípios que seguimos ao projetar as APIs.

O que fazer

Siga estas práticas recomendadas:

Fazer: usar estilos para visuais e modificadores para comportamentos

Use a API Styles para configuração visual (backgrounds, padding, bordas) e reserve modificadores para comportamentos como lógica de clique, detecção de gestos ou acessibilidade.

Fazer: expor parâmetros de estilo em sistemas de design

Para seus próprios componentes de sistema de design personalizado, exponha um objeto Style após o parâmetro do 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
}

Fazer: substituir parâmetros visuais por um estilo

Considere substituir parâmetros nos elementos combináveis por um único parâmetro Style. Exemplo:

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

Fazer: priorizar estilos para animações

Use o bloco animate integrado para estilização baseada em estado com animações para ganhos de desempenho em relação aos modificadores.

Fazer: aproveitar o "Last-write-wins"

Aproveite o fato de que as propriedades style substituem em vez de empilhar. Use isso para substituir as bordas ou os planos de fundo dos componentes padrão sem precisar de vários parâmetros.

Restrições

Os padrões a seguir não são recomendados:

Não fazer: usar estilos para lógica de interação

Não tente processar onClick ou detecção de gestos em um estilo. Os estilos são limitados a configurações visuais com base no estado. Portanto, eles não devem processar a lógica de negócios. Em vez disso, eles só devem ter um visual diferente com base no estado.

Não fazer: fornecer um estilo padrão como um parâmetro padrão

Os parâmetros de estilo sempre precisam ser declarados usando 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 um parâmetro "padrão", mescle o estilo de parâmetro recebido com o padrão 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
    }
}

Não fazer: fornecer parâmetros de estilo para elementos combináveis baseados em layout

Embora seja possível fornecer um estilo para qualquer elemento combinável, não é esperado que elementos combináveis baseados em layout ou elementos combináveis no nível da tela aceitem o estilo. Do ponto de vista do consumidor, não está claro o que um estilo faria nesse nível. Os estilos são projetados para componentes, não necessariamente layouts.

Não fazer: criar estilos na composição

Os CompositionLocals são lidos no ponto em que o estilo é definido, não onde ele é consumido. Quando o estilo é usado, o estado do CompositionLocal pode ter mudado, resultando em um 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)
}

Fazer: criar um estilo para mudanças de valor do subsistema

Por exemplo, ao alternar entre o modo claro e escuro, consulte os valores temáticos atuais (pelo CompositionLocal) para mudar o Style dinamicamente:

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

Fazer: trocar estilos inteiros quando o componente for fundamentalmente diferente nas definições de tema

É possível trocar objetos de estilo inteiros em um nível de tema se eles forem fundamentalmente diferentes.

Por exemplo, se você estiver criando um app que tenha temas diferentes por produto/página ou oferta e muitas propriedades de um estilo forem diferentes, é aceitável trocar conjuntos inteiros de estilos em um nível de 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
}