Conceitos básicos de estilos

Há três maneiras de adotar estilos no app:

  1. Use diretamente em componentes atuais que expõem um Style parâmetro.
  2. Aplique um estilo com Modifier.styleable em elementos combináveis de layout que não aceitam um Style parâmetro.
  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 oferecem suporte a muitas das mesmas propriedades que os modificadores. No entanto, 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 de conteúdo (interno) - contentPadding(all: Dp)
- contentPadding(horizontal: Dp, vertical: Dp)
- contentPadding(start: Dp, top: Dp, end: Dp, bottom: Dp)
- contentPaddingHorizontal(value: Dp) / contentPaddingVertical(value: Dp)
- contentPaddingStart(value: Dp) / contentPaddingTop(value: Dp) / contentPaddingEnd(value: Dp) / contentPaddingBottom(value: Dp)
Não
Padding externo (externo) - externalPadding(all: Dp)
- externalPadding(horizontal: Dp, vertical: Dp)
- externalPadding(start: Dp, top: Dp, end: Dp, bottom: Dp)
- externalPaddingHorizontal(value: Dp) / externalPaddingVertical(value: Dp)
- externalPaddingStart(value: Dp) / externalPaddingTop(value: Dp) / externalPaddingEnd(value: Dp) / externalPaddingBottom(value: Dp)
Não
Dimensões fillWidth()/fillHeight()/fillSize() e width, height e size (oferece suporte a frações Dp, DpSize ou Float). Não
Posicionamento Deslocamentos left/top/right/bottom. Não
Aparência visual
Carrega background e foreground (oferece suporte a 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/scaleY, rotationX/rotationY/rotationZ Não
Controle alpha, zIndex (ordem de empilhamento) e transformOrigin (ponto de ancoragem) Não
Tipografia
Estilo textStyle, fontSize, fontWeight, fontStyle e fontFamily Sim
Coloração contentColor e contentBrush. Isso também é usado para estilizar í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, você pode 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âmetro atual

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

Semelhante ao parâmetro style, você pode incluir propriedades como background, contentPadding ou externalPadding dentro da 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 para aplicar estilos baseados no estado. Para mais detalhes, consulte Estado e animações com estilos.

Definir um estilo independente

Você pode definir um estilo independente para fins de 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 Estado e animações com estilos.

O exemplo a seguir mostra como aplicar um estilo diretamente pelos parâmetros integrados de um componente ou por 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, o TealColor é o plano de fundo aplicado. Para o 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 para o 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
definido
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 de plano de fundo 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 um padding de 32.dp, exceto o padding esquerdo e direito, 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 estilos diferentes.

Herança de estilo

Determinadas 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 principal herdado para esse filho específico.

Propagação de estilo com parâmetros Style, styleable e direct
Figura 4. Propagação de estilo com parâmetros Style, styleable e 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 (mínima) Estilos principais Para propriedades que podem ser herdadas (tipografia/cor) transmitidas do pai.

Estilo principal

É possível definir propriedades de texto (como contentColor) do elemento combinável principal, 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 propriedade de elementos combináveis filhos.

Substituição de propriedades filhas

Também é possível definir o estilo em um elemento combinável Text específico. Se o elemento combinável principal tiver um estilo definido, o estilo definido no elemento combinável filho vai substituir o estilo do elemento 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
do elemento pai
Figura 6. Elementos combináveis filhos substituem propriedades principais.

Implementar propriedades de estilo personalizadas

É possível criar propriedades personalizadas que são mapeadas para definições de estilo atuais 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)
}

A criação de novas propriedades estilizadas não é compatível. Se o 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))
}