O Jetpack Compose oferece uma implementação do Material Design, um sistema de design abrangente para criar interfaces digitais. Os componentes do Material Design (botões, cards, chaves e assim por diante) são baseados nos Temas do Material Design, uma maneira sistemática de personalizar o Material Design para refletir melhor a marca do seu produto. Um Tema do Material Design é composto pelos atributos color, typography e shape. Quando você personaliza esses atributos, as mudanças são refletidas automaticamente nos componentes usados para criar o app.
O Jetpack Compose implementa esses conceitos com o elemento MaterialTheme
que pode ser composto:
MaterialTheme( colors = // ... typography = // ... shapes = // ... ) { // app content }
Configure os parâmetros transmitidos a MaterialTheme
para aplicar o tema no aplicativo.
Figura 1. A primeira captura de tela mostra um app que não configura
MaterialTheme
e, portanto, usa o estilo padrão. A segunda captura de tela mostra
um app que transmite parâmetros a MaterialTheme
para personalizar o estilo.
Cor
As cores são modeladas no Compose com a classe Color
, uma classe simples
de retenção de dados.
val Red = Color(0xffff0000) val Blue = Color(red = 0f, green = 0f, blue = 1f)
Embora você possa organizá-las da forma que quiser (como constantes de nível superior, em um singleton ou definidas in-line), é altamente recomendável especificar as cores no seu tema e recuperar as cores nele. Essa abordagem permite o suporte fácil ao tema escuro e a temas aninhados.
Figura 2. O sistema de cores do Material Design.
O Compose fornece a classe Colors
para modelar o
sistema de cores do Material Design. Colors
fornece
funções builder para criar conjuntos de cores claras
ou escuras:
private val Yellow200 = Color(0xffffeb46) private val Blue200 = Color(0xff91a4fc) // ... private val DarkColors = darkColors( primary = Yellow200, secondary = Blue200, // ... ) private val LightColors = lightColors( primary = Yellow500, primaryVariant = Yellow400, secondary = Blue700, // ... )
Depois de definir as Colors
, você poderá transmiti-las para um MaterialTheme
:
MaterialTheme( colors = if (darkTheme) DarkColors else LightColors ) { // app content }
Como usar cores de tema
É possível recuperar as Colors
fornecidas ao MaterialTheme
que pode ser composto usando
MaterialTheme.colors
.
Text( text = "Hello theming", color = MaterialTheme.colors.primary )
Cor da superfície e do conteúdo
Muitos componentes aceitam um par de cores e de cores de conteúdo:
Surface( color = MaterialTheme.colors.surface, contentColor = contentColorFor(color), // ... ) { /* ... */ } TopAppBar( backgroundColor = MaterialTheme.colors.primarySurface, contentColor = contentColorFor(backgroundColor), // ... ) { /* ... */ }
Isso permite não apenas definir a cor de um elemento combinável, mas também fornecer
uma cor padrão para o conteúdo, os elementos combináveis contidos nele. Muitos
elementos que podem ser compostos usam essa cor de conteúdo por padrão. Por exemplo, Text
baseia a
cor dele na cor do conteúdo do pai, e Icon
usa essa cor para definir a própria
tonalidade.
Figura 3. Definir cores diferentes para o plano de fundo produz cores de texto e ícone diferentes.
O método
contentColorFor()
recupera a cor "ativa"
adequada para todas as cores do tema. Por exemplo, se você definir uma cor primary
para o plano de fundo
em Surface
, ela usará essa função para definir onPrimary
como a cor do
conteúdo. Se você definir uma cor que não seja do tema para o plano de fundo, precisará especificar também uma
cor de conteúdo adequada. Use LocalContentColor
para recuperar a cor de conteúdo preferencial para o plano de fundo atual em uma
determinada posição na hierarquia.
Conteúdo Alfa
Muitas vezes, você quer variar o nível de ênfase no conteúdo para comunicar a importância e apresentar uma hierarquia visual. As recomendações de legibilidade do texto do Material Design aconselham a implementação de diferentes níveis de opacidade para transmitir níveis de importância distintos.
Isso é implementado pelo Jetpack Compose via LocalContentAlpha
.
É possível especificar um Alfa de conteúdo para uma hierarquia fornecendo um valor
para esse CompositionLocal
.
Os elementos
que podem ser compostos aninhados usam esse valor para aplicar o tratamento Alfa ao conteúdo.
Por exemplo, Text
e Icon
,
por padrão, usam a combinação de LocalContentColor
ajustada para usar LocalContentAlpha
. O Material Design especifica alguns valores Alfa padrão (high
, medium
,
disabled
), que são modelados pelo objeto ContentAlpha
.
// By default, both Icon & Text use the combination of LocalContentColor & // LocalContentAlpha. De-emphasize content by setting content alpha CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Text( // ... ) } CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { Icon( // ... ) Text( // ... ) }
Para saber mais sobre o CompositionLocal
, confira o Guia de dados com escopo local no
CompositionLocal.
Figura 4. Aplique diferentes níveis de ênfase no texto para comunicar visualmente
a hierarquia de informações. A primeira linha do texto é o título e tem as
informações mais importantes. Portanto, ela usa ContentAlpha.high
. A segunda
linha contém metadados menos importantes e, portanto, usa ContentAlpha.medium
.
Tema escuro
No Compose, você pode implementar temas claros e escuros ao fornecer conjuntos diferentes de
Colors
para o elemento MaterialTheme
que pode ser composto:
@Composable fun MyTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { MaterialTheme( colors = if (darkTheme) DarkColors else LightColors, /*...*/ content = content ) }
Neste exemplo, o MaterialTheme
é encapsulado na própria função que pode ser composta,
que aceita um parâmetro que especifica se é necessário ou não usar um tema escuro. Nesse
caso, a função recebe o valor padrão para darkTheme
consultando a
configuração do tema do dispositivo.
Você pode usar um código como este para conferir se o Colors
atual é claro ou escuro:
val isLightTheme = MaterialTheme.colors.isLight Icon( painterResource( id = if (isLightTheme) { R.drawable.ic_sun_24 } else { R.drawable.ic_moon_24 } ), contentDescription = "Theme" )
Sobreposições de elevação
No Material Design, as superfícies em temas escuros com elevações mais altas recebem sobreposições de elevação, que clareiam o plano de fundo. Quanto maior a elevação de uma superfície (elevando-a mais perto de uma fonte de luz implícita), mais clara ela se tornará.
Essas sobreposições são aplicadas automaticamente pela Surface
que pode ser composta ao usar
cores escuras e por qualquer outro elemento que pode ser composto do Material Design ao usar uma superfície:
Surface( elevation = 2.dp, color = MaterialTheme.colors.surface, // color will be adjusted for elevation /*...*/ ) { /*...*/ }
Figura 5. Os cards e a navegação inferior estão usando a cor surface
como plano de fundo. Como os cards e a navegação inferior estão em níveis de elevação
diferentes acima do plano de fundo, eles têm cores um pouco
distintas: os cards são mais claros que o plano de fundo, e a navegação inferior é
mais clara que os cards.
Para cenários personalizados, que não envolvem uma Surface
, use
LocalElevationOverlay
,
um CompositionLocal
que contém a
ElevationOverlay
usada por componentes
Surface
:
// Elevation overlays // Implemented in Surface (and any components that use it) val color = MaterialTheme.colors.surface val elevation = 4.dp val overlaidColor = LocalElevationOverlay.current?.apply( color, elevation )
Para desativar as sobreposições de elevação, forneça null
no ponto desejado em
uma hierarquia que pode ser composta:
MyTheme { CompositionLocalProvider(LocalElevationOverlay provides null) { // Content without elevation overlays } }
Tons de cores limitados
O Material Design recomenda aplicar tons de cores
limitados para temas
escuros, dando preferência à cor surface
em vez da cor primary
na
maioria dos casos. Os elementos que podem ser compostos do Material Design, como TopAppBar
e BottomNavigation
,
implementam esse comportamento por padrão.
Figura 6. Tema escuro do Material Design com tons de cor limitados. A barra de apps superior usa a cor principal no tema claro e a cor da superfície no tema escuro.
Para cenários personalizados, use a propriedade de extensão
primarySurface
:
Surface( // Switches between primary in light theme and surface in dark theme color = MaterialTheme.colors.primarySurface, /*...*/ ) { /*...*/ }
Tipografia
O Material Design define um sistema de tipos, incentivando você a usar um pequeno número de estilos com nomes semânticos.
Figura 7. O sistema de tipos do Material Design.
O Compose implementa o sistema de tipos com Typography
, TextStyle
e classes
relacionadas a fontes. O construtor Typography
oferece padrões a cada estilo para que você possa omitir qualquer um que não queira personalizar:
val raleway = FontFamily( Font(R.font.raleway_regular), Font(R.font.raleway_medium, FontWeight.W500), Font(R.font.raleway_semibold, FontWeight.SemiBold) ) val myTypography = Typography( h1 = TextStyle( fontFamily = raleway, fontWeight = FontWeight.W300, fontSize = 96.sp ), body1 = TextStyle( fontFamily = raleway, fontWeight = FontWeight.W600, fontSize = 16.sp ) /*...*/ ) MaterialTheme(typography = myTypography, /*...*/) { /*...*/ }
Se você quiser usar a mesma fonte, especifique o
defaultFontFamily parameter
e omita a fontFamily
dos elementos TextStyle
:
val typography = Typography(defaultFontFamily = raleway) MaterialTheme(typography = typography, /*...*/) { /*...*/ }
Como usar estilos de texto
TextStyle
s são acessados por MaterialTheme.typography
. Recupere os
TextStyle
s desta maneira:
Text( text = "Subtitle2 styled", style = MaterialTheme.typography.subtitle2 )
Figura 8. Use uma seleção de fontes e estilos para representar sua marca.
Forma
O Material Design define um sistema de formas, permitindo que você defina formas para componentes grandes, médios e pequenos.
Figura 9. O sistema de formas do Material Design.
O Compose implementa o sistema de formas com a
classe Shapes
, que permite
especificar uma CornerBasedShape
para cada categoria:
val shapes = Shapes( small = RoundedCornerShape(percent = 50), medium = RoundedCornerShape(0f), large = CutCornerShape( topStart = 16.dp, topEnd = 0.dp, bottomEnd = 0.dp, bottomStart = 16.dp ) ) MaterialTheme(shapes = shapes, /*...*/) { /*...*/ }
Muitos componentes usam essas formas por padrão. Por exemplo:
Button
,
TextField
e
FloatingActionButton
têm o valor padrão pequeno,
AlertDialog
segue o padrão médio e
ModalDrawer
,
o padrão grande. Consulte a
referência do esquema de formas
para ver o mapeamento completo.
Como usar formas
Shape
s são acessados por MaterialTheme.shapes
. Recupere os Shape
s com
um código como este:
Surface( shape = MaterialTheme.shapes.medium, /*...*/ ) { /*...*/ }
Figura 10. Use formas para expressar a marca ou o estado.
Estilos padrão
Não há um conceito equivalente no Compose de estilos padrão das visualizações do Android. Você pode fornecer funcionalidades semelhantes criando suas próprias funções que podem ser compostas "sobrecarregadas" para envolver os componentes do Material Design. Por exemplo, para criar um estilo de botão, envolva um botão na sua própria função que pode ser composta, definindo diretamente os parâmetros que você quer mudar e expondo outros como parâmetros à composição que os contêm.
@Composable fun MyButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.secondary ), onClick = onClick, modifier = modifier, content = content ) }
Sobreposições de tema
Você pode conseguir o equivalente às
sobreposições de tema das visualizações do Android no Compose aninhando
elementos MaterialTheme
que podem ser compostos. Como o
MaterialTheme
define as cores, a tipografia e as formas como o valor do tema atual, se um tema definir apenas um desses parâmetros, os outros manterão os valores padrão.
Além disso, ao migrar telas baseadas em visualização para o Compose, preste atenção aos usos do atributo android:theme
. É provável que você precise de um novo
MaterialTheme
nessa parte da árvore de IU do Compose.
Nesse exemplo, a tela de detalhes usa um PinkTheme
na maior parte da tela e um BlueTheme
para a seção relacionada. Veja a captura de tela e o código abaixo.
Figura 11. Temas aninhados.
@Composable fun DetailsScreen(/* ... */) { PinkTheme { // other content RelatedSection() } } @Composable fun RelatedSection(/* ... */) { BlueTheme { // content } }
Estados dos componentes
Os componentes do Material Design com que você pode interagir (clicar, alternar etc.) podem estar em diferentes estados visuais. Alguns estados: ativado, desativado, pressionado etc.
Os elementos que podem ser compostos geralmente têm um parâmetro enabled
. Defini-lo como false
impede
a interação e muda propriedades como cor e elevação para expressar visualmente
o estado do componente.
Figura 12. Botão com enabled = true
(à esquerda) e enabled = false
(à direita).
Na maioria dos casos, você pode confiar nos valores padrão de cor e elevação. Se você quiser configurar valores usados em estados diferentes, há classes e funções de conveniência disponíveis. Veja este exemplo:
Button( onClick = { /* ... */ }, enabled = true, // Custom colors for different states colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.secondary, disabledBackgroundColor = MaterialTheme.colors.onBackground .copy(alpha = 0.2f) .compositeOver(MaterialTheme.colors.background) // Also contentColor and disabledContentColor ), // Custom elevation for different states elevation = ButtonDefaults.elevation( defaultElevation = 8.dp, disabledElevation = 2.dp, // Also pressedElevation ) ) { /* ... */ }
Figura 13. Botão com enabled = true
(à esquerda) e enabled = false
(à direita), com valores de cor e elevação ajustados.
Ondulações
Os componentes do Material usam ondulações para indicar a interação. Se
você estiver usando MaterialTheme
na hierarquia, uma Ripple
será usada como a
Indication
padrão
dentro de modificadores, como
clickable
e
indication
.
Na maioria dos casos, você pode confiar na Ripple
padrão. Caso queira configurar
a aparência das ondulações, use
RippleTheme
para mudar propriedades como cor e Alfa.
É possível estender RippleTheme
e usar as
funções utilitárias
defaultRippleColor
e
defaultRippleAlpha
. Em seguida, você pode fornecer seu tema de ondulação personalizado na hierarquia usando
LocalRippleTheme
:
@Composable fun MyApp() { MaterialTheme { CompositionLocalProvider( LocalRippleTheme provides SecondaryRippleTheme ) { // App content } } } @Immutable private object SecondaryRippleTheme : RippleTheme { @Composable override fun defaultColor() = RippleTheme.defaultRippleColor( contentColor = MaterialTheme.colors.secondary, lightTheme = MaterialTheme.colors.isLight ) @Composable override fun rippleAlpha() = RippleTheme.defaultRippleAlpha( contentColor = MaterialTheme.colors.secondary, lightTheme = MaterialTheme.colors.isLight ) }
Figura 14. Botões com diferentes valores de ondulação fornecidos pelo RippleTheme
.
Saiba mais
Para saber mais sobre os temas do Material Design no Compose, consulte os recursos a seguir.
Codelabs
Vídeos
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Sistemas de design personalizados no Compose
- Migrar do Material 2 para o Material 3 no Compose
- Acessibilidade no Compose