Conceitos básicos de estilos

Há três maneiras de adotar estilos em todo o app:

  1. Use diretamente em componentes atuais que expõem um parâmetro Style.
  2. Aplique um estilo com Modifier.styleable em elementos combináveis de layout que não aceitam um parâmetro Style.
  3. No seu próprio sistema de design personalizado, use Modifier.styleable{} e exponha um parâmetro de estilo nos seus próprios componentes.

Propriedades disponíveis em "Estilos"

Os estilos são compatíveis com muitas das mesmas propriedades dos modificadores, mas nem tudo que é um modificador pode ser replicado com um estilo. Você ainda precisa de modificadores para determinados comportamentos, como interações, desenho personalizado ou empilhamento de propriedades.

Agrupamento Propriedades Herdadas por filhos
Layout e dimensionamento
Padding contentPadding (interna) e externalPadding (externa). Disponível nas variantes direcional, horizontal, vertical e em todos os lados. Não
Dimensões fillWidth/Height/Size() e width, height e size (aceita frações Dp, DpSize ou Float). Não
Posicionamento left/top/right/bottom de ajuste. Não
Aparência visual
Carrega background e foreground (compatível com Color ou Brush). Não
Bordas borderWidth, borderColor e borderBrush. Não
Forma shape Não, mas é usado em conjunto com outras propriedades. clip e border usam essa forma definida.
Sombras dropShadow, innerShadow Não
Transformações
Movimento espacial da camada de gráficos translationX, translationY, scaleX/Y, rotationX/Y/Z Não
Controle alpha, zIndex (ordem de empilhamento) e transformOrigin (ponto de pivô) Não
Tipografia
Estilo textStyle, fontSize, fontWeight, fontStyle e fontFamily Sim
Coloração contentColor e contentBrush. Isso também é usado para estilização de ícones. Sim
Parágrafo lineHeight, letterSpacing, textAlign, textDirection, lineBreak e hyphens. Sim
Decoração textDecoration, textIndent e baselineShift. Sim

Usar estilos diretamente em componentes com parâmetros de estilo

Os componentes que expõem um parâmetro Style permitem definir o estilo deles:

BaseButton(
    onClick = { },
    style = { }
) {
    BaseText("Click me")
}

Na lambda de estilo, é possível definir várias propriedades, como externalPadding ou background:

BaseButton(
    onClick = { },
    style = { background(Color.Blue) }
) {
    BaseText("Click me")
}

Para conferir a lista completa de propriedades compatíveis, consulte Propriedades disponíveis em Estilos.

Aplicar estilos usando modificadores para componentes sem parâmetros

Para componentes que não têm um parâmetro de estilo integrado, ainda é possível aplicar estilos com o modificador styleable. Essa abordagem também é útil ao desenvolver seus próprios componentes personalizados.

Row(
    modifier = Modifier.styleable { }
) {
    BaseText("Content")
}

Assim como o parâmetro style, é possível incluir propriedades como background ou padding no lambda.

Row(
    modifier = Modifier.styleable {
        background(Color.Blue)
    }
) {
    BaseText("Content")
}

Vários modificadores Modifier.styleable encadeados são aditivos com propriedades não herdadas no elemento combinável aplicado, comportando-se de maneira semelhante a vários modificadores que definem as mesmas propriedades. Para propriedades herdadas, elas são substituídas, e o último modificador styleable na cadeia define os valores.

Ao usar Modifier.styleable, talvez você também queira criar e fornecer um StyleState para ser usado com o modificador e aplicar um estilo com base no estado. Para mais detalhes, consulte Estados e animações com estilos.

Definir um estilo independente

É possível definir um estilo independente para reutilização:

val style = Style { background(Color.Blue) }

Em seguida, transmita esse estilo definido para o parâmetro de estilo de um elemento combinável ou com Modifier.styleable. Ao usar Modifier.styleable, também é necessário criar um objeto StyleState. StyleState é abordado em detalhes na documentação Estados e animações com estilos.

O exemplo a seguir mostra como aplicar um estilo diretamente usando os parâmetros integrados de um componente ou usando um Modifier.styleable:

val style = Style { background(Color.Blue) }

// built in parameter
BaseButton(onClick = { }, style = style) {
    BaseText("Button")
}

// modifier styleable
val styleState = remember { MutableStyleState(null) }
Column(
    Modifier.styleable(styleState, style)
) {
    BaseText("Column content")
}

Também é possível transmitir esse estilo para vários componentes:

val style = Style { background(Color.Blue) }

// built in parameter
BaseButton(onClick = { }, style = style) {
    BaseText("Button")
}
BaseText("Different text that uses the same style parameter", style = style)

// modifier styleable
val columnStyleState = remember { MutableStyleState(null) }
Column(
    Modifier.styleable(columnStyleState, style)
) {
    BaseText("Column")
}
val rowStyleState = remember { MutableStyleState(null) }
Row(
    Modifier.styleable(rowStyleState, style)
) {
    BaseText("Row")
}

Adicionar várias propriedades de estilo

É possível adicionar várias propriedades de estilo definindo propriedades diferentes em cada linha:

BaseButton(
    onClick = { },
    style = {
        background(Color.Blue)
        contentPaddingStart(16.dp)
    }
) {
    BaseText("Button")
}

As propriedades em estilos não são aditivas, ao contrário do estilo baseado em modificadores. Os estilos usam o último valor definido na lista de propriedades em um bloco de estilo. No exemplo a seguir, com o plano de fundo definido duas vezes, TealColor é o plano de fundo aplicado. Para padding, contentPaddingTop substitui o padding superior definido por contentPadding e não combina os valores.

BaseButton(
    style = {
        background(Color.Red)
        // Background of Red is now overridden with TealColor instead
        background(TealColor)
        // All directions of padding are set to 64.dp (top, start, end, bottom)
        contentPadding(64.dp)
        // Top padding is now set to 16.dp, all other paddings remain at 64.dp
        contentPaddingTop(16.dp)
    },
    onClick = {
        //
    }
) {
    BaseText("Click me!")
}

Botão com duas cores de plano de fundo definidas e duas substituições de contentPadding
Figura 1. Botão com duas cores de plano de fundo definidas e duas substituições de contentPadding.

Mesclar vários objetos de estilo

É possível criar vários objetos de estilo e transmiti-los ao parâmetro de estilo do elemento combinável.

val style1 = Style { background(TealColor) }
val style2 = Style { contentPaddingTop(16.dp) }

BaseButton(
    style = style1 then style2,
    onClick = {

    },
) {
    BaseText("Click me!")
}

Botão com cor de plano de fundo e contentPaddingTop
definidos
Figura 2. Botão com cor de plano de fundo e contentPaddingTop definidos.

Quando vários estilos especificam a mesma propriedade, a última propriedade definida é escolhida. Como as propriedades não são aditivas em estilos, o último padding transmitido substitui o contentPaddingHorizontal definido pelo contentPadding inicial. Além disso, a última cor de plano de fundo substitui a cor definida pelo estilo inicial transmitido.

val style1 = Style {
    background(Color.Red)
    contentPadding(32.dp)
}

val style2 = Style {
    contentPaddingHorizontal(8.dp)
    background(Color.LightGray)
}

BaseButton(
    style = style1 then style2,
    onClick = {

    },
) {
    BaseText("Click me!")
}

Nesse caso, o estilo aplicado tem um plano de fundo cinza-claro e padding de 32.dp, exceto o padding à esquerda e à direita, que tem um valor de 8.dp.

Botão com contentPadding substituído por diferentes
Styles
Figura 3. Botão com contentPadding substituído por diferentes estilos.

Herança de estilo

Algumas propriedades de estilo, como contentColor e propriedades relacionadas ao estilo de texto, são propagadas para os elementos combináveis filhos. Um estilo definido em um elemento combinável filho substitui o estilo herdado do elemento pai para esse filho específico.

Propagação de estilo com parâmetros Style, styleable e direct
Figura 4. Propagação de estilo com Style, styleable e parâmetros diretos.
Prioridade Método Efeito
1 (máxima) Argumentos diretos em um elemento combinável Substitui tudo. Por exemplo, Text(color = Color.Red)
2 Parâmetro de estilo Substituições de estilo local Text(style = Style { contentColor(Color.Red)}
3 Cadeia de modificadores Modifier.styleable{ contentColor(Color.Red) no próprio componente.
4 (mais baixa) Estilos principais Para propriedades que podem ser herdadas (tipografia/cor) transmitidas do elemento pai.

Estilo principal

É possível definir propriedades de texto (como contentColor) no elemento combinável pai, e elas são propagadas para todos os elementos combináveis Text filhos.

val styleState = remember { MutableStyleState(null) }
Column(
    modifier = Modifier.styleable(styleState) {
        background(Color.LightGray)
        val blue = Color(0xFF4285F4)
        val purple = Color(0xFFA250EA)
        val colors = listOf(blue, purple)
        contentBrush(Brush.linearGradient(colors))
    },
) {
    BaseText("Children inherit", style = { width(60.dp) })
    BaseText("certain properties")
    BaseText("from their parents")
}

Herança de propriedades de elementos combináveis filhos
Figura 5. Herança de propriedades de elementos combináveis filhos.

Substituição de propriedades por recursos filhos

Também é possível definir estilos em um elemento combinável Text específico. Se o combinável principal tiver um estilo definido, o estilo definido no combinável filho vai substituir o estilo do combinável principal.

val styleState = remember { MutableStyleState(null) }
Column(
    modifier = Modifier.styleable(styleState) {
        background(Color.LightGray)
        val blue = Color(0xFF4285F4)
        val purple = Color(0xFFA250EA)
        val colors = listOf(blue, purple)
        contentBrush(Brush.linearGradient(colors))
    },
) {
    BaseText("Children can ", style = {
        contentBrush(Brush.linearGradient(listOf(Color.Red, Color.Blue)))
    })
    BaseText("override properties")
    BaseText("set by their parents")
}

Os elementos combináveis filhos substituem as propriedades
principais
Figura 6. Os elementos combináveis filhos substituem as propriedades do pai.

Implementar propriedades de estilo personalizadas

É possível criar propriedades personalizadas que mapeiam definições de estilo usando funções de extensão no StyleScope, conforme mostrado no exemplo a seguir:

fun StyleScope.outlinedBackground(color: Color) {
    border(1.dp, color)
    background(color)
}

Aplique essa nova propriedade em uma definição de estilo:

val customExtensionStyle = Style {
    outlinedBackground(Color.Blue)
}

Não é possível criar novas propriedades estilizadas. Se o seu caso de uso exigir esse suporte, envie uma solicitação de recurso.

Ler valores CompositionLocal

É um padrão comum armazenar tokens do sistema de design em um CompositionLocal, para acessar as variáveis sem precisar transmiti-las como parâmetros. Os estilos podem acessar CompositionLocals para recuperar valores em todo o sistema dentro de um estilo:

val buttonStyle = Style {
    contentPadding(12.dp)
    shape(RoundedCornerShape(50))
    background(Brush.verticalGradient(LocalCustomColors.currentValue.background))
}