Embora o Material Design seja o sistema de design recomendado e o Jetpack Compose use uma implementação do Material Design, você tem outras opções. O Material Design foi criado com base em APIs públicas. Por isso, é possível criar seu próprio sistema de design da mesma maneira.
Há várias abordagens possíveis:
- Extensão do
MaterialThemecom outros valores de temas - Substituição de um ou mais sistemas do Material Design (
Colors,TypographyouShapes) por implementações personalizadas, mantendo os outros - Implementação de um sistema de design totalmente personalizado para
substituir o
MaterialTheme
Talvez você também queira continuar usando os componentes do Material Design em um sistema de design personalizado. É possível fazer isso, mas há alguns detalhes que precisam ser lembrados para atender à abordagem adotada.
Para saber mais sobre as construções de nível inferior e as APIs usadas no MaterialTheme
e em sistemas de design personalizados, consulte o guia Anatomia de um tema no Compose.
Como estender o tema do Material Design
O Compose modela os Temas do Material Design com cuidado para simplificar e seguir as diretrizes do Material Design com segurança de tipo. No entanto, é possível estender os conjuntos de cores, tipografias e formas com outros valores.
A abordagem mais simples é a adição de propriedades de extensão:
// Use with MaterialTheme.colorScheme.snackbarAction val ColorScheme.snackbarAction: Color @Composable get() = if (isSystemInDarkTheme()) Red300 else Red700 // Use with MaterialTheme.typography.textFieldInput val Typography.textFieldInput: TextStyle get() = TextStyle(/* ... */) // Use with MaterialTheme.shapes.card val Shapes.card: Shape get() = RoundedCornerShape(size = 20.dp)
Isso possibilita manter a consistência com as APIs de uso do MaterialTheme. Um exemplo disso,
definido pelo próprio Compose, é o
surfaceColorAtElevation,
que determina a cor da superfície que deve ser usada dependendo da elevação.
Outra abordagem possível é a definição de um tema estendido que "envolva" MaterialTheme e
os valores dele.
Você pode querer adicionar mais duas cores (caution e onCaution, um
amarelo usado para ações semiperigosas), por exemplo,
mantendo as cores do Material Design existentes:
@Immutable data class ExtendedColors( val caution: Color, val onCaution: Color ) val LocalExtendedColors = staticCompositionLocalOf { ExtendedColors( caution = Color.Unspecified, onCaution = Color.Unspecified ) } @Composable fun ExtendedTheme( /* ... */ content: @Composable () -> Unit ) { val extendedColors = ExtendedColors( caution = Color(0xFFFFCC02), onCaution = Color(0xFF2C2D30) ) CompositionLocalProvider(LocalExtendedColors provides extendedColors) { MaterialTheme( /* colors = ..., typography = ..., shapes = ... */ content = content ) } } // Use with eg. ExtendedTheme.colors.caution object ExtendedTheme { val colors: ExtendedColors @Composable get() = LocalExtendedColors.current }
Isso é semelhante às APIs de uso do MaterialTheme. Essa abordagem também é compatível com vários temas,
já que você pode aninhar ExtendedThemes da mesma forma que MaterialTheme.
Usar componentes do Material Design
Ao estender os Temas do Material Design, os valores MaterialTheme existentes são mantidos
e os componentes do Material Design mantêm padrões razoáveis.
Para usar valores estendidos em componentes, envolva-os nas suas próprias funções combináveis, definindo diretamente os valores que você quer mudar e expondo outros como parâmetros:
@Composable fun ExtendedButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( containerColor = ExtendedTheme.colors.caution, contentColor = ExtendedTheme.colors.onCaution /* Other colors use values from MaterialTheme */ ), onClick = onClick, modifier = modifier, content = content ) }
Será necessário substituir os usos de Button por ExtendedButton sempre que for
adequado.
@Composable fun ExtendedApp() { ExtendedTheme { /*...*/ ExtendedButton(onClick = { /* ... */ }) { /* ... */ } } }
Substituir subsistemas do Material
Em vez de estender os temas do Material Design, você pode substituir um ou mais
sistemas, Colors, Typography ou Shapes, por uma implementação personalizada,
mantendo os outros.
Você pode querer, por exemplo, substituir os sistemas de tipo e forma e, ao mesmo tempo, manter o sistema de cores:
@Immutable data class ReplacementTypography( val body: TextStyle, val title: TextStyle ) @Immutable data class ReplacementShapes( val component: Shape, val surface: Shape ) val LocalReplacementTypography = staticCompositionLocalOf { ReplacementTypography( body = TextStyle.Default, title = TextStyle.Default ) } val LocalReplacementShapes = staticCompositionLocalOf { ReplacementShapes( component = RoundedCornerShape(ZeroCornerSize), surface = RoundedCornerShape(ZeroCornerSize) ) } @Composable fun ReplacementTheme( /* ... */ content: @Composable () -> Unit ) { val replacementTypography = ReplacementTypography( body = TextStyle(fontSize = 16.sp), title = TextStyle(fontSize = 32.sp) ) val replacementShapes = ReplacementShapes( component = RoundedCornerShape(percent = 50), surface = RoundedCornerShape(size = 40.dp) ) CompositionLocalProvider( LocalReplacementTypography provides replacementTypography, LocalReplacementShapes provides replacementShapes ) { MaterialTheme( /* colors = ... */ content = content ) } } // Use with eg. ReplacementTheme.typography.body object ReplacementTheme { val typography: ReplacementTypography @Composable get() = LocalReplacementTypography.current val shapes: ReplacementShapes @Composable get() = LocalReplacementShapes.current }
Usar componentes do Material Design
Quando um ou mais sistemas de MaterialTheme forem substituídos, o uso de componentes
do Material Design inalterados poderá resultar em valores indesejados de cor, tipo ou forma.
Para usar valores de substituição em componentes, envolva-os nas suas próprias funções combináveis, definindo diretamente os valores do sistema relevante e expondo outros como parâmetros.
@Composable fun ReplacementButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( shape = ReplacementTheme.shapes.component, onClick = onClick, modifier = modifier, content = { ProvideTextStyle( value = ReplacementTheme.typography.body ) { content() } } ) }
Será necessário substituir os usos de Button por ReplacementButton sempre que for
adequado.
@Composable fun ReplacementApp() { ReplacementTheme { /*...*/ ReplacementButton(onClick = { /* ... */ }) { /* ... */ } } }
Implementar um sistema de design totalmente personalizado
É possível substituir os Temas do Material Design por um sistema de design totalmente personalizado.
O MaterialTheme oferece os seguintes sistemas:
Colors,TypographyeShapes: sistemas de Temas do Material DesignTextSelectionColors: cores usadas para seleção de texto porTexteTextFieldRippleeRippleTheme: implementação deIndicationpelo Material Design
Para continuar usando os componentes do Material Design, substitua alguns desses sistemas nos seus temas personalizados ou processe-os nos seus componentes para evitar comportamentos indesejados.
No entanto, os sistemas de design não estão limitados aos conceitos de que o Material Design depende. Você pode modificar os sistemas existentes e introduzir sistemas totalmente novos, com novas classes e tipos, para tornar outros conceitos compatíveis com temas.
No código a seguir, modelamos um sistema de cores personalizado com gradientes
(List<Color>), incluímos um sistema de tipos, apresentamos um novo sistema de elevação
e excluímos outros sistemas fornecidos pelo MaterialTheme:

@Immutable data class CustomColors( val content: Color, val component: Color, val background: List<Color> ) @Immutable data class CustomTypography( val body: TextStyle, val title: TextStyle ) @Immutable data class CustomElevation( val default: Dp, val pressed: Dp ) val LocalCustomColors = staticCompositionLocalOf { CustomColors( content = Color.Unspecified, component = Color.Unspecified, background = emptyList() ) } val LocalCustomTypography = staticCompositionLocalOf { CustomTypography( body = TextStyle.Default, title = TextStyle.Default ) } val LocalCustomElevation = staticCompositionLocalOf { CustomElevation( default = Dp.Unspecified, pressed = Dp.Unspecified ) } @Composable fun CustomTheme( /* ... */ content: @Composable () -> Unit ) { val customColors = CustomColors( content = Color(0xFFDD0D3C), component = Color(0xFFC20029), background = listOf(Color.White, Color(0xFFF8BBD0)) ) val customTypography = CustomTypography( body = TextStyle(fontSize = 16.sp), title = TextStyle(fontSize = 32.sp) ) val customElevation = CustomElevation( default = 4.dp, pressed = 8.dp ) CompositionLocalProvider( LocalCustomColors provides customColors, LocalCustomTypography provides customTypography, LocalCustomElevation provides customElevation, content = content ) } // Use with eg. CustomTheme.elevation.small object CustomTheme { val colors: CustomColors @Composable get() = LocalCustomColors.current val typography: CustomTypography @Composable get() = LocalCustomTypography.current val elevation: CustomElevation @Composable get() = LocalCustomElevation.current }
Usar componentes do Material Design
Quando não houver MaterialTheme, o uso de componentes do Material Design inalterados resultará
em comportamento de indicação e valores de cor, tipo e forma indesejados.
Para usar valores personalizados em componentes, envolva-os nas suas próprias funções de composição, definindo diretamente os valores do sistema relevante e expondo outros como parâmetros:
Recomendamos que você acesse os valores definidos usando o tema personalizado.
Se o tema não fornecer Color, TextStyle, Shape ou
outros sistemas, você também poderá fixá-los no código.
@Composable fun CustomButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( containerColor = CustomTheme.colors.component, contentColor = CustomTheme.colors.content, disabledContainerColor = CustomTheme.colors.content .copy(alpha = 0.12f) .compositeOver(CustomTheme.colors.component), disabledContentColor = CustomTheme.colors.content .copy(alpha = 0.38f) ), shape = ButtonShape, elevation = ButtonDefaults.elevatedButtonElevation( defaultElevation = CustomTheme.elevation.default, pressedElevation = CustomTheme.elevation.pressed /* disabledElevation = 0.dp */ ), onClick = onClick, modifier = modifier, content = { ProvideTextStyle( value = CustomTheme.typography.body ) { content() } } ) } val ButtonShape = RoundedCornerShape(percent = 50)
Se você introduziu novos tipos de classe, como List<Color> para representar gradientes, pode ser melhor implementar componentes do zero em vez de envolvê-los. Por exemplo, dê uma olhada em
JetsnackButton
na amostra do Jetsnack.
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Material Design 3 no Compose
- Migrar do Material 2 para o Material 3 no Compose
- Anatomia de um tema no Compose