Material Design 3 no Compose

O Jetpack Compose oferece uma implementação do Material You e do Material 3 Expressive, a próxima evolução do Material Design. O M3 Expressive é uma expansão do Material Design 3, incluindo atualizações baseadas em pesquisas para temas, componentes, movimento, tipografia e muito mais. Tudo foi projetado para ajudar você a criar produtos envolventes e desejáveis que os usuários vão adorar. Ele também é compatível com recursos de personalização do Material You, como cores dinâmicas. O M3 Expressive complementa o estilo visual e a interface do sistema do Android 16.

A seguir, demonstramos a implementação do Material Design 3 usando o app de exemplo Reply (link em inglês). O exemplo de resposta é totalmente baseado no Material Design 3.

App de exemplo Reply usando o Material Design 3
Figura 1. App de exemplo Reply usando o Material Design 3

Dependência

Para começar a usar o Material 3 no app Compose, adicione a dependência do Compose Material 3 aos arquivos build.gradle:

implementation "androidx.compose.material3:material3:$material3_version"

Depois que a dependência for adicionada, você poderá começar a adicionar sistemas do Material Design, incluindo cor, tipografia e forma, aos seus apps.

APIs experimentais

Algumas APIs do M3 são consideradas experimentais. Nesses casos, vai ser necessário ativar o nível da função ou do arquivo usando a anotação ExperimentalMaterial3Api:

// import androidx.compose.material3.ExperimentalMaterial3Api
@Composable
fun AppComposable() {
    // M3 composables
}

Temas do Material Design

Um tema do M3 contém os seguintes subsistemas: esquema de cores, tipografia e formas. Quando você personaliza esses valores, as mudanças são refletidas automaticamente nos componentes do M3 que você usa para criar o app.

Subsistemas do Material Design: cores, tipografia e formas
Figura 2. Subsistemas do Material Design: cores, tipografia e formas

O Jetpack Compose implementa esses conceitos com o elemento combinável MaterialTheme do M3:

MaterialTheme(
    colorScheme = /* ...
    typography = /* ...
    shapes = /* ...
) {
    // M3 app content
}

Para criar um tema para o conteúdo do aplicativo, defina o esquema de cores, a tipografia e as formas específicas do app.

Esquema de cores

A base de um esquema de cores é o conjunto de cinco cores principais. Cada uma dessas cores se relaciona a uma paleta tonal de 13 tons, que são usados pelos componentes do Material 3. Por exemplo, este é o esquema de cores do tema claro para Responder:

Esquema de cores claro do app de exemplo de resposta
Figura 3. Esquema de cores claras do app de exemplo Reply

Leia mais sobre o Esquema de cores e funções de cor.

Gerar esquemas de cores

Embora você possa criar um ColorScheme personalizado manualmente, geralmente é mais fácil gerar um usando as cores de origem da sua marca. A ferramenta Material Theme Builder permite fazer isso e, como opção, exportar o código de temas do Compose. Os seguintes arquivos são gerados:

  • O arquivo Color.kt contém as cores do seu tema com todas as funções definidas para cores de temas claro e escuro.

val md_theme_light_primary = Color(0xFF476810)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFC7F089)
// ..
// ..

val md_theme_dark_primary = Color(0xFFACD370)
val md_theme_dark_onPrimary = Color(0xFF213600)
val md_theme_dark_primaryContainer = Color(0xFF324F00)
// ..
// ..

  • O Theme.kt contém uma configuração para esquemas de cores claras e escuras e o tema do app.

private val LightColorScheme = lightColorScheme(
    primary = md_theme_light_primary,
    onPrimary = md_theme_light_onPrimary,
    primaryContainer = md_theme_light_primaryContainer,
    // ..
)
private val DarkColorScheme = darkColorScheme(
    primary = md_theme_dark_primary,
    onPrimary = md_theme_dark_onPrimary,
    primaryContainer = md_theme_dark_primaryContainer,
    // ..
)

@Composable
fun ReplyTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colorScheme =
        if (!darkTheme) {
            LightColorScheme
        } else {
            DarkColorScheme
        }
    MaterialTheme(
        colorScheme = colorScheme,
        content = content
    )
}

Para oferecer suporte a temas claros e escuros, use isSystemInDarkTheme(). Com base na configuração do sistema, defina qual esquema de cores usar: claro ou escuro.

Esquemas de cores dinâmicas

A cor dinâmica é a parte principal do Material You, em que um algoritmo deriva cores personalizadas do plano de fundo de um usuário para serem aplicadas aos apps e à interface do sistema. Essa paleta de cores é usada como ponto de partida para gerar esquemas de cores claras e escuras.

Tema dinâmico do app de exemplo Reply com base no plano de fundo (esquerda) e tema padrão do app (direita)
Figura 4. Temas dinâmicos do app de exemplo Reply com base no plano de fundo (esquerda) e no tema padrão do app (direita)

As cores dinâmicas estão disponíveis no Android 12 e em versões mais recentes. Se a cor dinâmica estiver disponível, será possível configurar um ColorScheme dinâmico. Caso contrário, volte a usar um ColorScheme personalizado claro ou escuro.

O ColorScheme fornece funções do builder para criar um esquema de cores dinâmicas claro ou escuro:

// Dynamic color is available on Android 12+
val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
val colors = when {
    dynamicColor && darkTheme -> dynamicDarkColorScheme(LocalContext.current)
    dynamicColor && !darkTheme -> dynamicLightColorScheme(LocalContext.current)
    darkTheme -> DarkColorScheme
    else -> LightColorScheme
}

Uso das cores

Você pode acessar as cores do tema Material no seu app via MaterialTheme.colorScheme:

Text(
    text = "Hello theming",
    color = MaterialTheme.colorScheme.primary
)

Cada função de cor pode ser usada em vários lugares, dependendo do estado, do destaque e da ênfase do componente.

  • A cor primária é a cor de base, usada para os principais componentes, como botões em destaque, estados ativos e a tonalidade de superfícies elevadas.
  • A cor secundária principal é usada para componentes menos proeminentes na interface, como ícones de filtro, e aumenta a oportunidade de expressão de cores.
  • A cor terciária principal é usada para derivar as funções de tons contrastantes que podem ser usados para equilibrar cores primárias e secundárias ou chamar mais atenção para um elemento.

O design do app de exemplo Reply usa a cor do contêiner não primário sobre o contêiner primário para enfatizar o item selecionado.

Contêiner principal e campos de texto com a cor "on-primary-container".
Figura 5. Contêiner principal e campos de texto com a cor "on-primary-container".

Card(
    colors = CardDefaults.cardColors(
        containerColor =
        if (isSelected) MaterialTheme.colorScheme.primaryContainer
        else
            MaterialTheme.colorScheme.surfaceVariant
    )
) {
    Text(
        text = "Dinner club",
        style = MaterialTheme.typography.bodyLarge,
        color =
        if (isSelected) MaterialTheme.colorScheme.onPrimaryContainer
        else MaterialTheme.colorScheme.onSurface,
    )
}

Aqui você pode ver no gaveta de navegação de resposta como as cores secundárias e terciárias do contêiner são usadas em contraste para criar ênfase e destaque.

Combinação de contêiner terciário e on-tertiary-container para o botão de ação flutuante.
Figura 6. Combinação de contêiner terciário e on-tertiary-container para o botão de ação flutuante.

Tipografia

O Material Design 3 define uma escala de tipo, incluindo estilos de texto que foram adaptados do Material Design 2. A nomenclatura e o agrupamento foram simplificados para: display, headline, title, body e label, com tamanhos grandes, médios e pequenos para cada um.

Escala tipográfica padrão para o Material Design 3
Figura 7. Escala de tipografia padrão para o Material Design 3
M3 Tamanho da fonte/altura da linha padrão
displayLarge Roboto 57/64
displayMedium Roboto 45/52
displaySmall Roboto 36/44
headlineLarge Roboto 32/40
headlineMedium Roboto 28/36
headlineSmall Roboto 24/32
titleLarge New- Roboto Medium 22/28
titleMedium Roboto Medium 16/24
titleSmall Roboto Medium 14/20
bodyLarge Roboto 16/24
bodyMedium Roboto 14/20
bodySmall Roboto 12/16
labelLarge Roboto Medium 14/20
labelMedium Roboto Medium 12/16
labelSmall New Roboto Medium, 11/16

Definir tipografia

O Compose oferece a classe Typography do M3, além das classes TextStyle e relacionadas a fontes, para modelar a escala de tipos do Material 3. O construtor Typography oferece padrões a cada estilo para que você possa omitir os parâmetros que não quiser personalizar:

val replyTypography = Typography(
    titleLarge = TextStyle(
        fontWeight = FontWeight.SemiBold,
        fontSize = 22.sp,
        lineHeight = 28.sp,
        letterSpacing = 0.sp
    ),
    titleMedium = TextStyle(
        fontWeight = FontWeight.SemiBold,
        fontSize = 16.sp,
        lineHeight = 24.sp,
        letterSpacing = 0.15.sp
    ),
    // ..
)
// ..

Corpo grande, corpo médio e rótulo médio para diferentes usos de tipografia.
Figura 8. Body large, body medium e label medium para diferentes usos de tipografia.

Seu produto provavelmente não vai precisar de todos os 15 estilos padrão da escala de tipografia do Material Design. Neste exemplo, cinco tamanhos são escolhidos para um conjunto reduzido, e o restante é omitido.

É possível personalizar a tipografia mudando os valores padrão de TextStyle e propriedades relacionadas à fonte, como fontFamily e letterSpacing.

bodyLarge = TextStyle(
    fontWeight = FontWeight.Normal,
    fontFamily = FontFamily.SansSerif,
    fontStyle = FontStyle.Italic,
    fontSize = 16.sp,
    lineHeight = 24.sp,
    letterSpacing = 0.15.sp,
    baselineShift = BaselineShift.Subscript
),

Depois de definir a Typography, transmita-a ao MaterialTheme do M3:

MaterialTheme(
    typography = replyTypography,
) {
    // M3 app Content
}

Usar estilos de texto

É possível recuperar a tipografia fornecida ao elemento combinável MaterialTheme do M3 usando MaterialTheme.typography:

Text(
    text = "Hello M3 theming",
    style = MaterialTheme.typography.titleLarge
)
Text(
    text = "you are learning typography",
    style = MaterialTheme.typography.bodyMedium
)

Leia mais sobre as diretrizes do Material Design em aplicação de tipografia (link em inglês).

Formas

As superfícies do Material Design podem ser mostradas em diferentes formas. As formas podem ser usadas para chamar atenção, identificar os componentes, comunicar o estado e expressar a marca.

A escala de forma define o estilo dos cantos do contêiner, oferecendo uma variedade de arredondamentos, de quadrado a totalmente circular.

Definir formas

O Compose oferece a classe Shapes do M3 com parâmetros expandidos para oferecer suporte a novas formas do M3. A escala de formas do M3 é mais parecida com a escala tipográfica, permitindo um intervalo expressivo de formas em toda a interface.

Há diferentes tamanhos de formas:

  • Extrapequena
  • Pequeno
  • Média
  • Grande
  • Extragrande

Por padrão, cada forma tem um valor padrão, mas é possível substituir esses valores:

val replyShapes = Shapes(
    extraSmall = RoundedCornerShape(4.dp),
    small = RoundedCornerShape(8.dp),
    medium = RoundedCornerShape(12.dp),
    large = RoundedCornerShape(16.dp),
    extraLarge = RoundedCornerShape(24.dp)
)

Depois de definir a Shapes, você poderá transmiti-la para um MaterialTheme do M3:

MaterialTheme(
    shapes = replyShapes,
) {
    // M3 app Content
}

Usar formas

É possível personalizar a escala de forma para todos os componentes no MaterialTheme ou fazer isso por componente.

Aplicar forma média e grande com valores padrão:

Card(shape = MaterialTheme.shapes.medium) { /* card content */ }
FloatingActionButton(
    shape = MaterialTheme.shapes.large,
    onClick = {
    }
) {
    /* fab content */
}

Forma média para o card e forma grande para o botão de ação flutuante no app de exemplo de resposta.
Figura 9. Forma média para o card e forma grande para o botão de ação flutuante no app de exemplo Reply

Outras duas formas estão disponíveis para o uso (RectangleShape e CircleShape) no Compose. A forma retangular não tem um raio de borda, e a forma circular mostra as bordas circulares completas:

Card(shape = RectangleShape) { /* card content */ }
Card(shape = CircleShape) { /* card content */ }

Os exemplos abaixo demonstram alguns dos componentes com valores de forma padrão aplicados a eles:

Valores de formas padrão para todos os componentes do Material 3.
Figura 10. Valores de formas padrão para todos os componentes do Material 3.

Saiba mais sobre as diretrizes do Material Design em aplicar formato.

Ênfase

A ênfase no M3 é fornecida usando variações de cor e suas combinações. No M3, há duas maneiras de adicionar ênfase à sua interface:

  • Usando superfície, variante de superfície e plano de fundo junto com as cores da superfície e das variantes da superfície do sistema de cores expandido do M3. Por exemplo, a superfície pode ser usada com a variante de superfície, e vice-versa, para fornecer diferentes níveis de ênfase.
Usar combinações de cores neutras para dar ênfase.
Figura 11. Usar combinações de cores neutras para dar ênfase.
  • Usar diferentes pesos de fonte para texto. Acima, você viu que é possível fornecer pesos personalizados à nossa escala de tipos para dar uma ênfase diferente.

bodyLarge = TextStyle(
    fontWeight = FontWeight.Bold
),
bodyMedium = TextStyle(
    fontWeight = FontWeight.Normal
)

Elevação

O Material 3 representa a elevação usando principalmente sobreposições de cores tonais. Essa é uma nova maneira de distinguir contêineres e superfícies, aumentando a elevação tonal usando um tom mais proeminente, além de sombras.

Elevação de tonalidade com elevação de sombra
Figura 12. Elevação de tonalidade com elevação de sombraE

As sobreposições de elevação em temas escuros também mudaram para sobreposições de cores tonais no Material 3. A cor de sobreposição vem do slot de cores principal.

Elevação de sombra x elevação tonal no Material Design 3
Figura 13. Elevação de sombra x elevação tonal no Material Design 3

A Superfície do M3, o elemento combinável por trás da maioria dos componentes do M3, inclui suporte para a elevação de tonalidade e sombra:

Surface(
    modifier = Modifier,
    tonalElevation = /*...
    shadowElevation = /*...
) {
    Column(content = content)
}

Componentes do Material Design

O Material Design vem com um conjunto avançado de componentes do Material (como botões, chips, cards, barra de navegação) que já seguem o Material Theming e ajudam você a criar apps bonitos com o Material Design. Você pode começar a usar componentes com propriedades padrão imediatamente.

Button(onClick = { /*..*/ }) {
    Text(text = "My Button")
}

O M3 oferece muitas versões dos mesmos componentes para serem usadas em diferentes funções, de acordo com a ênfase e a atenção.

Ênfase do botão do FAB, do principal até o botão de texto
Figura 14. Ênfase do botão do FAB, do principal até o botão de texto
  • Um botão de ação flutuante estendido para a ação de maior ênfase:

ExtendedFloatingActionButton(
    onClick = { /*..*/ },
    modifier = Modifier
) {
    Icon(
        imageVector = Icons.Default.Edit,
        contentDescription = stringResource(id = R.string.edit),
    )
    Text(
        text = stringResource(id = R.string.add_entry),
    )
}

  • Um botão preenchido para uma ação de alta ênfase:

Button(onClick = { /*..*/ }) {
    Text(text = stringResource(id = R.string.view_entry))
}

  • Um botão de texto para uma ação de baixa ênfase:

TextButton(onClick = { /*..*/ }) {
    Text(text = stringResource(id = R.string.replated_articles))
}

Saiba mais sobre botões e outros componentes do Material Design. O Material 3 oferece uma grande variedade de conjuntos de componentes, como botões, barras de apps e componentes de navegação, que são projetados especificamente para diferentes casos de uso e tamanhos de tela.

O Material também oferece vários componentes de navegação que ajudam a implementar a navegação, dependendo de diferentes tamanhos e estados de tela.

O NavigationBar é usado para dispositivos compactos quando você quer segmentar cinco ou menos destinos:

NavigationBar(modifier = Modifier.fillMaxWidth()) {
    Destinations.entries.forEach { replyDestination ->
        NavigationBarItem(
            selected = selectedDestination == replyDestination,
            onClick = { },
            icon = { }
        )
    }
}

NavigationRail é usado para tablets ou smartphones de tamanho pequeno a médio no modo paisagem. Ela oferece ergonomia aos usuários e melhora a experiência do usuário nesses dispositivos.

NavigationRail(
    modifier = Modifier.fillMaxHeight(),
) {
    Destinations.entries.forEach { replyDestination ->
        NavigationRailItem(
            selected = selectedDestination == replyDestination,
            onClick = { },
            icon = { }
        )
    }
}

Mostruário de respostas de BottomNavigationBar(esquerda) e NavigationRail(direita)
Figura 15. Mostruário de respostas de BottomNavigationBar (esquerda) e NavigationRail (direita)

Responda usando os dois temas padrão para oferecer uma experiência imersiva em todos os tamanhos de dispositivo.

NavigationDrawer é usado em tablets de tamanho médio a grande, em que há espaço suficiente para mostrar detalhes. É possível usar PermanentNavigationDrawer ou ModalNavigationDrawer com NavigationRail.

PermanentNavigationDrawer(modifier = Modifier.fillMaxHeight(), drawerContent = {
    Destinations.entries.forEach { replyDestination ->
        NavigationRailItem(
            selected = selectedDestination == replyDestination,
            onClick = { },
            icon = { },
            label = { }
        )
    }
}) {
}

Resposta: vitrine da gaveta de navegação permanente
Figura 16. Exibição do Reply da gaveta de navegação permanente

As opções de navegação melhoram a experiência do usuário, a ergonomia e a acessibilidade. Saiba mais sobre os componentes de navegação do Material no codelab adaptável do Compose.

Personalizar a estilização de um componente

O M3 incentiva a personalização e a flexibilidade. Todos os componentes têm cores padrão aplicadas, mas expõem APIs flexíveis para personalizar as cores, se necessário.

A maioria dos componentes, como cards e botões, oferece um objeto padrão que expõe interfaces de cor e elevação que podem ser modificadas para personalizar o componente:

val customCardColors = CardDefaults.cardColors(
    contentColor = MaterialTheme.colorScheme.primary,
    containerColor = MaterialTheme.colorScheme.primaryContainer,
    disabledContentColor = MaterialTheme.colorScheme.surface,
    disabledContainerColor = MaterialTheme.colorScheme.onSurface,
)
val customCardElevation = CardDefaults.cardElevation(
    defaultElevation = 8.dp,
    pressedElevation = 2.dp,
    focusedElevation = 4.dp
)
Card(
    colors = customCardColors,
    elevation = customCardElevation
) {
    // m3 card content
}

Leia mais sobre personalizar o Material 3.

IU do sistema

Alguns aspectos do Material You vêm do novo estilo visual e da interface do sistema no Android 12 e versões mais recentes. As duas áreas principais em que há mudanças são no Ripple e no Overscroll. Nenhum trabalho adicional é necessário para implementar essas mudanças.

Ondulação

O Ripple agora usa um brilho sutil para iluminar superfícies quando pressionado. O Compose Material Ripple usa um RippleDrawable da plataforma disponível internamente no Android. Portanto, a ondulação brilhante está disponível no Android 12 ou posterior para todos os componentes do Material .

Ondulação em M2 x M3
Figura 17. Ondulação no M2 x M3

Overscroll

O Overscroll agora usa um efeito de alongamento na borda dos contêineres de rolagem. O alongamento de rolagem é ativado por padrão na rolagem dos elementos combináveis do contêiner (por exemplo, LazyColumn, LazyRow e LazyVerticalGrid) no Compose Foundation 1.1.0 e versões mais recentes, independentemente do nível da API.

Overscroll usando efeito de alongamento na borda do contêiner
Figura 18. Overscroll usando efeito de alongamento na borda do contêiner

Acessibilidade

Os padrões de acessibilidade integrados aos componentes do Material Design foram criados para fornecer uma base para o design de produtos inclusivo. Entender a acessibilidade do seu produto pode melhorar a usabilidade para todos os usuários, incluindo aqueles com baixa visão, cegueira, deficiências auditivas, cognitivas, motoras ou deficiências situacionais (como um braço quebrado).

Acessibilidade de cor

A cor dinâmica foi criada para atender aos padrões de acessibilidade de contraste de cores. O sistema de paletas de tons é fundamental para tornar qualquer esquema de cores acessível por padrão.

O sistema de cores do Material Design oferece valores de tom e medições padrão que podem ser usados para atender às proporções de contraste acessíveis.

App de exemplo de resposta: paletas tonais primária, secundária e terciária (de cima para baixo)
Figura 19. App de exemplo de resposta: paletas tonais primárias, secundárias e terciárias (de cima para baixo)

Todos os componentes do Material e a estilização dinâmica já usam as funções de cor acima de um conjunto de paletas tonais, selecionadas para atender aos requisitos de acessibilidade. No entanto, se você estiver personalizando componentes, use funções de cores adequadas e evite incompatibilidades.

Use cores não primárias sobre a primária e contêineres não primários sobre os primários. Faça o mesmo para outros destaques e cores neutras. Assim, é possível fornecer um contraste acessível ao usuário.

O uso de um contêiner terciário sobre o primário dá ao usuário um botão de contraste ruim:

// ✅ Button with sufficient contrast ratio
Button(
    onClick = { },
    colors = ButtonDefaults.buttonColors(
        containerColor = MaterialTheme.colorScheme.primary,
        contentColor = MaterialTheme.colorScheme.onPrimary
    )
) {
}

// ❌ Button with poor contrast ratio
Button(
    onClick = { },
    colors = ButtonDefaults.buttonColors(
        containerColor = MaterialTheme.colorScheme.tertiaryContainer,
        contentColor = MaterialTheme.colorScheme.primaryContainer
    )
) {
}

Contraste suficiente (à esquerda) x contraste ruim (à direita)
Figura 20. Contraste suficiente (à esquerda) versus contraste ruim (à direita)

Acessibilidade de tipografia

A escala de tipos do M3 atualiza a rampa e os valores de tipo estático para oferecer uma estrutura simplificada, mas dinâmica, de categorias de tamanho que são dimensionadas em vários dispositivos.

Por exemplo, no M3, o Display Small pode receber valores diferentes dependendo do contexto do dispositivo, como um smartphone ou um tablet.

Telas grandes

O Material oferece orientações sobre layouts adaptáveis e dispositivos dobráveis para tornar seus apps acessíveis e melhorar a ergonomia dos usuários que usam dispositivos grandes.

O Material Design oferece diferentes tipos de navegação para ajudar você a proporcionar uma experiência melhor do usuário em dispositivos grandes.

Saiba mais sobre as diretrizes de qualidade de apps para telas grandes do Android e confira nosso exemplo de resposta para design adaptável e acessível.

Saiba mais

Para saber mais sobre os temas do Material Design no Compose, confira os seguintes recursos:

Apps de exemplo

Documentos

Referência da API e código-fonte

Vídeos