1. Introdução
Neste codelab, você vai aprender sobre a aplicação de temas no Jetpack Compose usando o Material Design 3. Você também vai aprender sobre os principais elementos básicos dos esquemas de cores, da tipografia e das formas do Material Design 3, que ajudam a aplicar temas no seu app de maneira personalizada e acessível.
Além disso, você vai conhecer a compatibilidade com temas dinâmicos e diferentes níveis de ênfase.
O que você vai aprender
Neste codelab, você vai aprender o seguinte:
- Principais aspectos da aplicação de temas do Material 3
- Esquemas de cores do Material 3 e como gerar temas para seu app
- Como oferecer suporte à aplicação de temas dinâmicos e claros/escuros para seu app
- Tipografia e formas para personalizar seu app
- Componentes do Material 3 e personalização para estilizar seu app
O que você vai criar
Neste codelab, você vai criar um tema para um app de e-mail cliente chamado Reply. Vamos começar com um aplicativo sem estilo usando o tema de referência e usar o que você aprendeu para aplicar um tema no aplicativo e oferecer suporte a temas escuros.
Ponto de partida padrão do app com o tema de referência.
Você vai criar seu tema com esquema de cores, tipografia e formas e, depois, vai aplicá-lo à lista de e-mails e à página de detalhes do app. Você também vai oferecer suporte a temas dinâmicos no app. Ao final do codelab, seu app vai permitir o uso de temas dinâmicos e de cores.
Ponto final do codelab de aplicação de temas de cores claras e temas dinâmicos claros.
Ponto final do codelab de aplicação de temas de cores escuras e temas dinâmicos escuros.
O que é necessário
- A versão mais recente do Android Studio.
- Experiência básica com a linguagem Kotlin
- Conhecimentos básicos sobre o Jetpack Compose
- Conhecimento básico dos layouts do Compose, como Row, Column e Modifier
2. Etapas da configuração
Nesta etapa, você vai fazer o download do código completo do app Reply que será estilizado neste codelab.
Acessar o código
O código deste codelab pode ser encontrado no repositório codelab-android-compose do GitHub. Para cloná-lo, execute:
$ git clone https://github.com/android/codelab-android-compose
Outra opção é fazer o download de dois arquivos ZIP:
Conferir o app de exemplo
O código que você acabou de baixar contém todos os codelabs disponíveis do Compose. Para concluir este codelab, abra o projeto ThemingCodelab no Android Studio.
Recomendamos que você comece com o código na ramificação principal (main) e siga todas as etapas do codelab no seu ritmo. Você pode executar qualquer uma das versões no Android Studio a qualquer momento, modificando a ramificação do git do projeto.
Conhecer o código inicial
O código principal contém um pacote de interface, que tem os seguintes pacotes e arquivos principais com os quais você vai interagir:
MainActivity.kt
: atividade do ponto de entrada em que você inicia o app Reply.com.example.reply.ui.theme
: este pacote contém temas, tipografia e esquemas de cores. Você vai adicionar temas do Material Design a este pacote.com.example.reply.ui.components
: contém os componentes personalizados do app, como "Itens de lista", "Barras de apps" etc. Você vai aplicar temas a esses componentes.ReplyApp.kt
: é a principal função combinável em que a árvore da interface vai começar. Você vai aplicar temas de nível superior neste arquivo.
Este codelab se concentra em arquivos de pacote ui
.
3. Aplicação de temas com o Material 3
O Jetpack Compose oferece uma implementação do Material Design, um sistema de design abrangente para criar interfaces digitais. Os componentes desse sistema (botões, cards, chaves e assim por diante) são criados com base em Temas do Material Design. Assim, eles oferecem uma maneira sistemática de personalizar o Material Design para refletir melhor a marca do seu produto.
O tema do Material 3 é composto pelos seguintes subsistemas para adicionar temas ao app: esquema de cores, tipografia e formas. Quando você personaliza esses valores, as mudanças são refletidas automaticamente nos componentes do M3 usados para criar o app. Vamos nos aprofundar em cada subsistema e implementá-los no app de exemplo.
Subsistema de cores, tipografia e formas do Material 3.
4. Esquemas de cores
A base de um esquema de cores é o conjunto de cinco cores principais, cada uma relacionada a uma paleta tonal de 13 tons, usada pelos componentes do Material 3.
Cinco cores principais de valor de referência para criar um tema do M3.
Cada cor de destaque (primária, secundária e terciária) é fornecida em quatro cores compatíveis de tons diferentes para pareamento, definição de ênfase e expressão visual.
Quatro cores tonais das cores de destaque primária, secundária e terciária.
Da mesma forma, as cores neutras também são divididas em quatro tons compatíveis usados para superfícies e plano de fundo. Eles também são importantes para destacar ícones de texto quando colocados em qualquer superfície.
Quatro cores tonais neutras de referência.
Leia mais sobre o Esquema de cores e funções de cor.
Como 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 possibilita fazer isso e, como opção, exportar o código de temas do Compose.
Você pode escolher a cor que quiser, mas, para nosso caso de uso, vai usar a cor primária padrão do app Reply, a #825500
. Clique em Primárias na seção Cores primárias à esquerda e adicione o código no seletor de cores.
Adicionando o código de cor primária no Material Theme Builder.
Depois que você adiciona a cor primária no Material Theme Builder, o tema a seguir e a opção de exportação aparecem no canto superior direito. Neste codelab, você vai exportar o tema no Jetpack Compose.
Criador de Temas do Material Design com a opção de exportação no canto superior direito.
A cor primária #825500
gera o tema a seguir, que você vai adicionar ao app. O Material 3 oferece uma ampla variedade de funções de cor para expressar com flexibilidade o estado, o destaque e a ênfase de um componente.
Esquema de cores claras e escuras exportado da cor primária.
O arquivo The Color.kt
gerado contém as cores do seu tema com todos as funções definidos para cores de temas claro e escuro.
Color.kt
package com.example.reply.ui.theme
import androidx.compose.ui.graphics.Color
val md_theme_light_primary = Color(0xFF825500)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFFFDDB3)
val md_theme_light_onPrimaryContainer = Color(0xFF291800)
val md_theme_light_secondary = Color(0xFF6F5B40)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFFBDEBC)
val md_theme_light_onSecondaryContainer = Color(0xFF271904)
val md_theme_light_tertiary = Color(0xFF51643F)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFD4EABB)
val md_theme_light_onTertiaryContainer = Color(0xFF102004)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFFFBFF)
val md_theme_light_onBackground = Color(0xFF1F1B16)
val md_theme_light_surface = Color(0xFFFFFBFF)
val md_theme_light_onSurface = Color(0xFF1F1B16)
val md_theme_light_surfaceVariant = Color(0xFFF0E0CF)
val md_theme_light_onSurfaceVariant = Color(0xFF4F4539)
val md_theme_light_outline = Color(0xFF817567)
val md_theme_light_inverseOnSurface = Color(0xFFF9EFE7)
val md_theme_light_inverseSurface = Color(0xFF34302A)
val md_theme_light_inversePrimary = Color(0xFFFFB951)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF825500)
val md_theme_light_outlineVariant = Color(0xFFD3C4B4)
val md_theme_light_scrim = Color(0xFF000000)
val md_theme_dark_primary = Color(0xFFFFB951)
val md_theme_dark_onPrimary = Color(0xFF452B00)
val md_theme_dark_primaryContainer = Color(0xFF633F00)
val md_theme_dark_onPrimaryContainer = Color(0xFFFFDDB3)
val md_theme_dark_secondary = Color(0xFFDDC2A1)
val md_theme_dark_onSecondary = Color(0xFF3E2D16)
val md_theme_dark_secondaryContainer = Color(0xFF56442A)
val md_theme_dark_onSecondaryContainer = Color(0xFFFBDEBC)
val md_theme_dark_tertiary = Color(0xFFB8CEA1)
val md_theme_dark_onTertiary = Color(0xFF243515)
val md_theme_dark_tertiaryContainer = Color(0xFF3A4C2A)
val md_theme_dark_onTertiaryContainer = Color(0xFFD4EABB)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF1F1B16)
val md_theme_dark_onBackground = Color(0xFFEAE1D9)
val md_theme_dark_surface = Color(0xFF1F1B16)
val md_theme_dark_onSurface = Color(0xFFEAE1D9)
val md_theme_dark_surfaceVariant = Color(0xFF4F4539)
val md_theme_dark_onSurfaceVariant = Color(0xFFD3C4B4)
val md_theme_dark_outline = Color(0xFF9C8F80)
val md_theme_dark_inverseOnSurface = Color(0xFF1F1B16)
val md_theme_dark_inverseSurface = Color(0xFFEAE1D9)
val md_theme_dark_inversePrimary = Color(0xFF825500)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFFFFB951)
val md_theme_dark_outlineVariant = Color(0xFF4F4539)
val md_theme_dark_scrim = Color(0xFF000000)
val seed = Color(0xFF825500)
O arquivo The Theme.kt
gerado contém uma configuração para esquemas de cores claras e escuras e o tema do app. Ele também contém a principal função combinável de aplicação de temas, AppTheme()
.
Theme.kt
package com.example.reply.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable
private val LightColors = lightColorScheme(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer,
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer,
tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
errorContainer = md_theme_light_errorContainer,
onError = md_theme_light_onError,
onErrorContainer = md_theme_light_onErrorContainer,
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant,
outline = md_theme_light_outline,
inverseOnSurface = md_theme_light_inverseOnSurface,
inverseSurface = md_theme_light_inverseSurface,
inversePrimary = md_theme_light_inversePrimary,
surfaceTint = md_theme_light_surfaceTint,
outlineVariant = md_theme_light_outlineVariant,
scrim = md_theme_light_scrim,
)
private val DarkColors = darkColorScheme(
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer,
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary,
secondaryContainer = md_theme_dark_secondaryContainer,
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
errorContainer = md_theme_dark_errorContainer,
onError = md_theme_dark_onError,
onErrorContainer = md_theme_dark_onErrorContainer,
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
outline = md_theme_dark_outline,
inverseOnSurface = md_theme_dark_inverseOnSurface,
inverseSurface = md_theme_dark_inverseSurface,
inversePrimary = md_theme_dark_inversePrimary,
surfaceTint = md_theme_dark_surfaceTint,
outlineVariant = md_theme_dark_outlineVariant,
scrim = md_theme_dark_scrim,
)
@Composable
fun AppTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable() () -> Unit
) {
val colors = if (!useDarkTheme) {
LightColors
} else {
DarkColors
}
MaterialTheme(
colorScheme = colors,
content = content
)
}
O principal elemento para implementar temas no Jetpack Compose é o elemento combinável MaterialTheme
.
Una o elemento combinável MaterialTheme()
à função AppTheme()
, que usa dois parâmetros:
useDarkTheme
: esse parâmetro está vinculado à funçãoisSystemInDarkTheme()
para observar as configurações do sistema relativas à aplicação de temas e aplicar o tema claro ou escuro. Para manter o app em um tema claro ou escuro manualmente, transmita um valor booleano para ouseDarkTheme
.content
: o conteúdo ao qual o tema será aplicado.
Theme.kt
@Composable
fun AppTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable() () -> Unit
) {
val colors = if (!useDarkTheme) {
LightColors
} else {
DarkColors
}
MaterialTheme(
colorScheme = colors,
content = content
)
}
Se você tentar executar o app agora, vai notar que ele tem a mesma aparência. Mesmo que você tenha importado nosso novo esquema de cores com novas cores para a aplicação de tema, os temas de linha de base ainda vão aparecer, porque você não aplicou o tema ao app Compose.
App com o tema de referência quando nenhum tema é aplicado.
Para aplicar o novo tema, em MainActivity.kt
, envolva o ReplyApp
combinável principal com a função de tema principal, AppTheme()
.
MainActivity.kt
setContent {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
AppTheme {
ReplyApp(/*..*/)
}
}
Você também vai atualizar as funções de visualização para conferir o tema aplicado às visualizações do app. Una o elemento combinável ReplyApp
a ReplyAppPreview()
com o AppTheme
para aplicar temas às visualizações.
Como você tem os temas claro e escuro do sistema definidos nos parâmetros de visualização, os dois serão mostrados.
MainActivity.kt
@Preview(
uiMode = Configuration.UI_MODE_NIGHT_YES,
name = "DefaultPreviewDark"
)
@Preview(
uiMode = Configuration.UI_MODE_NIGHT_NO,
name = "DefaultPreviewLight"
)
@Composable
fun ReplyAppPreview() {
AppTheme {
ReplyApp(
replyHomeUIState = ReplyHomeUIState(
emails = LocalEmailsDataProvider.allEmails
)
)
}
}
Se você executar o app agora, as cores do tema importado vão aparecer no app, em vez do tema de referência.
App com tema de referência (à esquerda).
App com tema de cor importado (à direita).
Visualizações de apps claros e escuros com temas de cores importados.
O Material 3 oferece suporte para esquemas de cores claras e escuras. Você uniu o app ao tema importado. Os componentes do Material 3 estão usando funções de cor padrão.
Vamos aprender sobre as funções de cor e usá-las antes de serem adicionadas ao app.
Funções de cor e acessibilidade
Cada função de cor pode ser usada em vários lugares, dependendo do estado, do destaque e da ênfase do componente.
Cores de funções primárias, secundárias e terciárias.
Primária é a cor de base, que é usada para os principais componentes, como botões em destaque e estados ativos.
A cor secundária principal é usada para componentes menos proeminentes na interface, por exemplo, ícones de filtro.
Já a cor terciária principal é usada para fornecer tons contrastantes, e cores neutras são usadas para o plano de fundo e as superfícies do app.
O sistema de cores do Material Design oferece valores de tom padrão e medições que podem ser usadas para atender às proporções de contraste acessíveis. Use cores não primárias sobre a primária, contêineres não primários sobre os primários e o mesmo para outros destaques e cores neutras. Assim, é possível fornecer um contraste acessível ao usuário.
Para mais informações, consulte Funções de cor e acessibilidade.
Elevação de tonalidade e sombra
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 com um tom mais proeminente, além de sombras.
Elevação tonal no nível 2, que usa cores do slot de cores primárias.
As sobreposições de elevação em temas escuros também mudaram para sobreposições de cores tonais no Material Design 3. A cor de sobreposição vem do slot de cores principal.
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)
}
Como adicionar cores ao app
Se você executar o app, as cores exportadas serão mostradas nos locais em que os componentes estão usando cores padrão. Agora que sabemos sobre funções e uso de cores, vamos definir um tema para o app com as funções de cor corretas.
App com tema de cores e componentes usando as funções de cor padrão.
Cores da superfície
Na tela inicial, comece encapsulando o elemento combinável do app principal em uma Surface()
para fornecer a base à inclusão do conteúdo. Abra MainActivity.kt
e envolva o elemento combinável ReplyApp()
com Surface
.
Você também vai fornecer uma elevação de 5 dp para dar à superfície uma cor tonal de espaço principal. Isso ajuda a fornecer contraste em relação ao item da lista e à barra de pesquisa na parte de cima. Por padrão, a elevação de tonalidade e sombra para a superfície é 0 dp.
MainActivity.kt
AppTheme {
Surface(tonalElevation = 5.dp) {
ReplyApp(
replyHomeUIState = uiState,
// other parameters
)
}
}
Se você executar o aplicativo agora e verificar a lista e a página de detalhes, vai notar que a superfície tonal está aplicada em todo o app.
Plano de fundo do app sem cor de superfície nem cor tonal (à esquerda).
Plano de fundo do app com cor de superfície e cor tonal aplicadas (à direita).
Cores da barra de apps
Nossa barra de pesquisa personalizada na parte de cima não tem um plano de fundo claro, como solicitado pelo design. Por padrão, ela retorna à superfície base padrão. Você pode fornecer um plano de fundo para oferecer uma separação clara.
Barra de pesquisa personalizada sem plano de fundo (à esquerda).
Barra de pesquisa personalizada com plano de fundo (à direita).
Agora você vai editar ui/components/ReplyAppBars.kt
, que contém a barra de apps. Você vai adicionar MaterialTheme.colorScheme.background
ao Modifier
do elemento combinável Row
.
ReplyAppBars.kt
@Composable
fun ReplySearchBar(modifier: Modifier = Modifier) {
Row(
modifier = modifier
.fillMaxWidth()
.padding(16.dp)
.background(MaterialTheme.colorScheme.background),
verticalAlignment = Alignment.CenterVertically
) {
// Search bar content
}
}
Agora aparece uma separação clara entre a superfície tonal e a barra de apps com a cor do plano de fundo.
Barra de pesquisa com cor de fundo sobre a superfície tonal.
Cores do botão de ação flutuante
Botão de ação flutuante grande sem aplicação de tema (à esquerda).
Botão de ação flutuante grande com aplicação de tema e cores terciárias (direita).
Na tela inicial, você pode melhorar a aparência do botão de ação flutuante (FAB, na sigla em inglês) para que ele se destaque como um botão call-to-action. Para implementar isso, é necessário aplicar uma cor de destaque terciária à tela.
No arquivo ReplyListContent.kt
, atualize a containerColor
da cor do FAB para tertiaryContainer
e a cor do conteúdo para onTertiaryContainer
a fim de manter a acessibilidade e o contraste de cores.
ReplyListContent.kt
ReplyInboxScreen(/*..*/) {
// Email list content
LargeFloatingActionButton(
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
contentColor = MaterialTheme.colorScheme.onTertiaryContainer
){
/*..*/
}
}
Execute o app para conferir o FAB com tema aplicado. Neste codelab, você vai usar um LargeFloatingActionButton
.
Cores de cards
A lista de e-mails na tela inicial usa um componente card. Por padrão, é um card preenchido que usa a cor da variante de superfície na cor do contêiner para fornecer uma separação clara entre a superfície e a cor do card. O Compose também oferece implementações de ElevatedCard
e OutlinedCard
.
É possível destacar alguns itens importantes, fornecendo tons de cores secundários. Para modificar ui/components/ReplyEmailListItem.kt
, atualize a cor do contêiner do card usando CardDefaults.cardColors()
para e-mails importantes:
ReplyEmailListItem.kt
Card(
modifier = modifier
.padding(horizontal = 16.dp, vertical = 4.dp)
.semantics { selected = isSelected }
.clickable { navigateToDetail(email.id) },
colors = CardDefaults.cardColors(
containerColor = if (email.isImportant)
MaterialTheme.colorScheme.secondaryContainer
else MaterialTheme.colorScheme.surfaceVariant
)
){
/*..*/
}
Destaque o item da lista usando a cor secundária do contêiner na superfície tonal.
Cor dos itens da lista de detalhes
Agora, você definiu um tema para sua tela inicial. Clique em qualquer um dos itens da lista de e-mails para conferir a página de detalhes.
Página de detalhes padrão sem aplicação de tema nos itens de lista (à esquerda).
Item da lista de detalhes com aplicação de tema de plano de fundo (à direita).
Como nenhuma cor é aplicada ao item da lista, ele volta à cor de superfície tonal padrão. Você vai aplicar uma cor de plano de fundo ao item da lista para criar uma separação e adicionar padding para criar uma espaçamento ao redor do plano de fundo.
ReplyEmailThreadItem.kt
@Composable
fun ReplyEmailThreadItem(
email: Email,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier
.fillMaxWidth()
.padding(16.dp)
.background(MaterialTheme.colorScheme.background)
.padding(20.dp)
) {
// List item content
}
}
Ao fornecer o plano de fundo, você tem uma separação clara entre a superfície tonal e os itens da lista.
Agora, as páginas iniciais e de detalhes têm funções e uso de cores corretos. Vamos conferir como seu app pode usar cores dinâmicas para fornecer uma experiência ainda mais personalizada e coesa.
5. Como adicionar cores dinâmicas ao app
Cor dinâmica é a parte principal do Material 3, em que um algoritmo deriva cores personalizadas do plano de fundo de um usuário para ser aplicado aos apps e à interface do sistema dele.
A aplicação de temas dinâmicos torna seus apps mais personalizados. Ela também oferece aos usuários uma experiência coesa e integrada com o tema do sistema.
As cores dinâmicas estão disponíveis no Android 12 e em versões mais recentes. Se uma cor dinâmica está disponível, você pode configurar um esquema de cores dinâmicas usando dynamicDarkColorScheme()
ou dynamicLightColorScheme()
. Caso contrário, volte a usar um ColorScheme
claro ou escuro padrão.
Substitua o código da função AppTheme
no arquivo Theme.kt
pelo código abaixo:
Theme.kt
@Composable
fun AppTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val context = LocalContext.current
val colors = when {
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) -> {
if (useDarkTheme) dynamicDarkColorScheme(context)
else dynamicLightColorScheme(context)
}
useDarkTheme -> DarkColors
else -> LightColors
}
MaterialTheme(
colorScheme = colors,
content = content
)
}
Tema dinâmico retirado do plano de fundo do Android 13.
Ao executar o app agora, a aplicação de temas dinâmicos aparece usando o plano de fundo padrão do Android 13.
A barra de status também pode ser estilizada dinamicamente, dependendo do esquema de cores usado para definir o tema do app.
App sem aplicação de cores na barra de status (esquerda).
App com aplicação de cores na barra de status (à direita).
Para atualizar a cor da barra de status de acordo com a cor primária do tema, adicione a cor da barra de status após a seleção do esquema de cores no elemento combinável AppTheme
:
Theme.kt
@Composable
fun AppTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
// color scheme selection code
// Add primary status bar color from chosen color scheme.
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colors.primary.toArgb()
WindowCompat
.getInsetsController(window, view)
.isAppearanceLightStatusBars = useDarkTheme
}
}
MaterialTheme(
colorScheme = colors,
content = content
)
}
Ao executar o app, a barra de status aparece com o tema de cores primárias. Você também pode testar temas dinâmicos claros e escuros mudando o tema escuro do sistema.
Tema dinâmico claro (esquerda) e escuro (direita) aplicado ao plano de fundo padrão do Android 13.
Até agora, você aplicou cores ao app, o que melhorou a aparência dele. No entanto, podemos notar que todo o texto no app é do mesmo tamanho. Então, agora, você pode adicionar tipografia ao app.
6. Tipografia
O Material Design 3 define uma escala de tipos. A nomenclatura e o agrupamento foram simplificados para: display, headline, title, body e label, com tamanhos grande, médio e pequeno para cada um.
Escala de tipos do Material 3.
Como definir a tipografia
O Compose oferece a classe Typography
do M3 e as classes TextStyle
e font-related
já existentes para modelar a escala de tipos do Material 3.
O construtor de tipografia oferece padrões a cada estilo para que você possa omitir os parâmetros que não quiser personalizar. Para mais informações, consulte os estilos e valores padrão de tipografia
Você vai usar cinco estilos de tipografia no seu app: headlineSmall
, titleLarge
, bodyLarge
, bodyMedium
e labelMedium
. Esses estilos se aplicam à tela inicial e à tela de detalhes.
Tela mostrando o uso de tipografia do título, rótulo e estilo do corpo.
Em seguida, acesse o pacote ui/theme
e abra Type.kt
. Adicione o seguinte código para fornecer sua própria implementação de alguns estilos de texto em vez de valores padrão:
Type.kt
val typography = Typography(
headlineSmall = TextStyle(
fontWeight = FontWeight.SemiBold,
fontSize = 24.sp,
lineHeight = 32.sp,
letterSpacing = 0.sp
),
titleLarge = TextStyle(
fontWeight = FontWeight.Normal,
fontSize = 18.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
bodyLarge = TextStyle(
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.15.sp
),
bodyMedium = TextStyle(
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.25.sp
),
labelMedium = TextStyle(
fontWeight = FontWeight.SemiBold,
fontSize = 12.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
)
Sua tipografia foi definida. Para adicioná-la ao tema, transmita-o ao elemento combinável MaterialTheme()
dentro do AppTheme
:
Theme.kt
@Composable
fun AppTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable() () -> Unit
) {
// dynamic theming content
MaterialTheme(
colorScheme = colors,
typography = typography,
content = content
)
}
Como trabalhar com tipografia
Assim como nas cores, você acessa o estilo de tipografia do tema atual usando MaterialTheme.typography
. Dessa forma, você tem a instância de tipografia para usar toda a tipografia definida em Type.kt
.
Text(
text = "Hello M3 theming",
style = MaterialTheme.typography.titleLarge
)
Text(
text = "you are learning typography",
style = MaterialTheme.typography.bodyMedium
)
Seu produto provavelmente não vai precisar de todos os 15 estilos padrão da escala de tipografia do Material Design. Neste codelab, são escolhidos cinco tamanhos e o restante é omitido.
Como você não aplicou tipografia aos elementos combináveis Text()
, todo o texto volta para Typography.bodyLarge
por padrão.
Tipografia da lista na tela inicial
Em seguida, aplique a tipografia à função ReplyEmailListItem
em ui/components/ReplyEmailListItem.kt
para diferenciar títulos de rótulos:
ReplyEmailListItem.kt
Text(
text = email.sender.firstName,
style = MaterialTheme.typography.labelMedium
)
Text(
text = email.createdAt,
style = MaterialTheme.typography.labelMedium
)
Text(
text = email.subject,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(top = 12.dp, bottom = 8.dp),
)
Text(
text = email.body,
maxLines = 2,
style = MaterialTheme.typography.bodyLarge,
overflow = TextOverflow.Ellipsis
)
Tela inicial sem aplicação de tipografia (à esquerda).
Tela inicial com aplicação de tipografia (à direita).
Tipografia da lista de detalhes
Da mesma forma, você vai adicionar a tipografia na tela de detalhes atualizando todos os elementos combináveis de texto de ReplyEmailThreadItem
em ui/components/ReplyEmailThreadItem.kt
:
ReplyEmailThreadItem.kt
Text(
text = email.sender.firstName,
style = MaterialTheme.typography.labelMedium
)
Text(
text = stringResource(id = R.string.twenty_mins_ago),
style = MaterialTheme.typography.labelMedium
)
Text(
text = email.subject,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(top = 12.dp, bottom = 8.dp),
)
Text(
text = email.body,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Tela de detalhes sem aplicação de tipografia (à esquerda).
Tela de detalhes com aplicação de tipografia (à direita).
Como personalizar a tipografia
Com o Compose, é muito fácil personalizar seu estilo de texto ou fornecer uma fonte personalizada. Você pode modificar o TextStyle
para personalizar o tipo de fonte, a família de fontes, o espaçamento entre letras etc.
Você pode mudar o estilo de texto no arquivo theme/Type.kt
, que será refletido em todos os componentes que o utilizam.
Atualize fontWeight
para SemiBold
e lineHeight
para 32.sp
em titleLarge
, que é usado para o assunto no item da lista. Isso dá mais ênfase ao assunto e deixa clara a separação.
Type.kt
...
titleLarge = TextStyle(
fontWeight = FontWeight.SemiBold,
fontSize = 18.sp,
lineHeight = 32.sp,
letterSpacing = 0.0.sp
),
...
Como aplicar tipografia personalizada ao texto do assunto.
7. 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.
Como definir formas
O Compose oferece à classe Shapes
parâmetros expandidos para implementar novas formas do M3. A escala de formas do M3, assim como a escala de tipos, permite um intervalo expressivo de formas em toda a interface.
Há diferentes tamanhos na escala de formas:
- Muito pequena
- Pequena
- Médio
- Grande
- Extra grande
Cada forma tem um valor padrão que pode ser substituído. No seu app, você vai usar a forma média para modificar o item de lista, mas também pode declarar outras formas. Crie um novo arquivo com o nome Shape.kt
no pacote ui/theme
e adicione o código para as formas:
Shape.kt
package com.example.reply.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp
val shapes = Shapes(
extraSmall = RoundedCornerShape(4.dp),
small = RoundedCornerShape(8.dp),
medium = RoundedCornerShape(16.dp),
large = RoundedCornerShape(24.dp),
extraLarge = RoundedCornerShape(32.dp)
)
Agora que você definiu as shapes
, transmita-as ao MaterialTheme
do M3 como fez para cores e tipografia:
Theme.kt
@Composable
fun AppTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable() () -> Unit
) {
// dynamic theming content
MaterialTheme(
colorScheme = colors,
typography = typography,
shapes = shapes,
content = content
)
}
Como trabalhar com formas
Assim como nas cores e na tipografia, é possível aplicar formas a componentes do Material Design usando o MaterialTheme.shape
, que oferece a instância de Shape
para acessar as formas do Material Design.
Muitos componentes do Material Design já têm formas padrão, mas é possível fornecer e aplicar suas próprias formas aos componentes por meio de slots disponíveis.
Card(shape = MaterialTheme.shapes.medium) { /* card content */ }
FloatingActionButton(shape = MaterialTheme.shapes.large) { /* fab content */}
Mapeamento de componentes do Material Design usando diferentes tipos de formas.
Confira o mapeamento de formas em todos os componentes na documentação sobre formas.
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.
Você também pode aplicar formas aos seus componentes usando Modifiers
que assumem formas, por exemplo, Modifier.clip
, Modifier.background e Modifier.border
.
Forma da barra de apps
Queremos que a barra de apps tenha um plano de fundo com cantos arredondados:
A TopAppBar
está usando uma Row
com uma cor de plano de fundo. Para atingir o segundo plano de canto arredondado, defina a forma do plano de fundo transmitindo CircleShape
ao modificador em segundo plano:
ReplyAppBars.kt
@Composable
fun ReplySearchBar(modifier: Modifier = Modifier) {
Row(
modifier = modifier
.fillMaxWidth()
.padding(16.dp)
.background(
MaterialTheme.colorScheme.background,
CircleShape
),
verticalAlignment = Alignment.CenterVertically
) {
// Search bar content
}
}
Forma dos itens da lista de detalhes
Na tela inicial, você está utilizando um card que usa Shape.Medium
por padrão. No entanto, para nossa página de detalhes, você usou uma coluna com cor de plano de fundo. Para ter uma aparência uniforme da lista, aplique uma forma média a ela.
Coluna de itens de lista de detalhes sem forma no item da lista (à esquerda) e forma média na lista (à direita).
ReplyEmailThreadItem.kt
@Composable
fun ReplyEmailThreadItem(
email: Email,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier
.fillMaxWidth()
.padding(8.dp)
.background(
MaterialTheme.colorScheme.background,
MaterialTheme.shapes.medium
)
.padding(16.dp)
) {
// List item content
}
}
Agora, quando o app é executado, aparece um item de lista de tela com a forma medium
.
8. Ênfase
A ênfase na interface ajuda a destacar alguns conteúdos, por exemplo, quando você quer diferenciar o título das legendas. A ênfase no M3 usa variações de cor e suas combinações. Há duas maneiras de adicionar ênfase:
- 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.
As variantes de superfície também podem ser usadas com cores de destaque para dar menos ênfase do que as cores de destaque principais, mas ainda podem ser acessadas e acompanhar a taxa de contraste.
Funções de cor de superfície, plano de fundo e variante de superfície
- Usar diferentes pesos de fonte para texto. Como você viu na seção de tipografia, é possível fornecer pesos personalizados à sua escala de tipos para dar uma ênfase diferente.
Agora, atualize ReplyEmailListItem.kt
para fornecer uma diferença de ênfase usando a variante de superfície. Por padrão, a cor do conteúdo do card depende do plano de fundo.
Você vai mudar a cor do elemento combinável do texto de hora e do e texto do corpo para onSurfaceVariant
. Isso reduz a ênfase em relação a onContainerColors
, que é aplicado por padrão a elementos combináveis do texto do título e do assunto.
Texto de hora e corpo com a mesma ênfase em comparação com o assunto e o título (à esquerda).
Hora e corpo com ênfase reduzida em comparação com o assunto e o título (à direita).
ReplyEmailListItem.kt
Text(
text = email.createdAt,
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = email.body,
maxLines = 2,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
overflow = TextOverflow.Ellipsis
)
Para o card de e-mail importante com o plano de fundo secondaryContainer
, todas as cores de texto são onSecondaryContainer
por padrão. Para os outros e-mails, o plano de fundo é surfaceVariant,
. Portanto, todo o texto tem onSurfaceVariant
como padrão.
9. Parabéns
Parabéns! Você concluiu este codelab. Você implementou a aplicação de temas do Material Design no Compose usando cores, tipografia e formas. Além disso, usou cores dinâmicas para definir o tema do aplicativo e oferecer uma experiência personalizada.
Fim dos resultados da aplicação de temas com cores dinâmicas e tema de cores aplicados.
A seguir
Confira nossos outros codelabs no Programa de treinamentos do Compose:
Leia mais
- Guia de temas do Compose
- Temas do Material Design para o Compose (link em inglês)
Apps de exemplo
- App de exemplo Reply com temas completos do Material 3
- JetChat demonstrando a aplicação de temas dinâmicos