Anatomia de um tema no Compose

Os temas no Jetpack Compose são compostos por várias construções de nível mais baixo e APIs relacionadas. Isso pode ser visto no código-fonte do MaterialTheme e também pode ser aplicado em sistemas de design personalizado.

Classes do sistema de temas

Um tema normalmente é composto por vários subsistemas que agrupam recursos visuais e conceitos comportamentais. Esses sistemas podem ser modelados com classes que têm valores de tema.

Por exemplo, o MaterialTheme inclui ColorScheme (sistema de cores), Typography (sistema de tipografia) e Shapes (sistema de formas).

@Immutable
data class ColorSystem(
    val color: Color,
    val gradient: List<Color>
    /* ... */
)

@Immutable
data class TypographySystem(
    val fontFamily: FontFamily,
    val textStyle: TextStyle
)
/* ... */

@Immutable
data class CustomSystem(
    val value1: Int,
    val value2: String
    /* ... */
)

/* ... */

Locais de composição do sistema de temas

As classes do sistema de temas são fornecidas implicitamente à árvore de composição como instâncias CompositionLocal. Isso possibilita que os valores de temas sejam referenciados estaticamente em funções que podem ser compostas.

Para saber mais sobre a CompositionLocal, confira o Guia de dados com escopo local na CompositionLocal.

val LocalColorSystem = staticCompositionLocalOf {
    ColorSystem(
        color = Color.Unspecified,
        gradient = emptyList()
    )
}

val LocalTypographySystem = staticCompositionLocalOf {
    TypographySystem(
        fontFamily = FontFamily.Default,
        textStyle = TextStyle.Default
    )
}

val LocalCustomSystem = staticCompositionLocalOf {
    CustomSystem(
        value1 = 0,
        value2 = ""
    )
}

/* ... */

Função do tema

A função do tema é o ponto de entrada e a API principal. Ela cria instâncias da CompositionLocals do sistema de temas usando valores reais que qualquer lógica exige. Esses valores são fornecidos na árvore de composição com CompositionLocalProvider. O parâmetro content possibilita que elementos que podem ser compostos aninhados acessem valores de tema relacionados à hierarquia.

@Composable
fun Theme(
    /* ... */
    content: @Composable () -> Unit
) {
    val colorSystem = ColorSystem(
        color = Color(0xFF3DDC84),
        gradient = listOf(Color.White, Color(0xFFD7EFFF))
    )
    val typographySystem = TypographySystem(
        fontFamily = FontFamily.Monospace,
        textStyle = TextStyle(fontSize = 18.sp)
    )
    val customSystem = CustomSystem(
        value1 = 1000,
        value2 = "Custom system"
    )
    /* ... */
    CompositionLocalProvider(
        LocalColorSystem provides colorSystem,
        LocalTypographySystem provides typographySystem,
        LocalCustomSystem provides customSystem,
        /* ... */
        content = content
    )
}

Objeto do tema

O acesso a sistemas de temas é feito usando um objeto com propriedades de conveniência. Para consistência, o objeto tende a receber o mesmo nome da função do tema. As propriedades simplesmente recebem a composição local atual.

// Use with eg. Theme.colorSystem.color
object Theme {
    val colorSystem: ColorSystem
        @Composable
        get() = LocalColorSystem.current
    val typographySystem: TypographySystem
        @Composable
        get() = LocalTypographySystem.current
    val customSystem: CustomSystem
        @Composable
        get() = LocalCustomSystem.current
    /* ... */
}