Jetpack Compose ofrece una implementación de Material Design, un sistema de diseño integral para crear interfaces digitales. Los componentes de Material Design (botones, tarjetas, interruptores, etcétera) se basan en los temas de Material, que son una forma sistemática de personalizar Material Design para reflejar mejor la marca de tu producto. Un tema de Material contiene atributos de color, tipografía y forma. Cuando personalizas estos atributos, tus cambios se reflejan automáticamente en los componentes que usas para compilar tu app.
Jetpack Compose implementa esos conceptos con el elemento MaterialTheme que admite composición:
MaterialTheme( colors = // ... typography = // ... shapes = // ... ) { // app content }
Configura los parámetros que pasas a MaterialTheme para aplicar un tema a tu aplicación.
Color
Los colores se modelan en Compose con la clase Color, una clase que contiene datos.
val Red = Color(0xffff0000) val Blue = Color(red = 0f, green = 0f, blue = 1f)
Si bien puedes organizarlos como te guste (como constantes de nivel superior, dentro de un singleton o intercalado definido), te recomendamos que especifiques los colores en tu tema y los recuperes desde allí. Este enfoque permite admitir el tema oscuro y los temas anidados.
Compose proporciona la clase Colors para modelar el sistema de color de Material. Colors proporciona funciones de compilador para crear conjuntos de colores claros u oscuros:
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, // ... )
Después de definir tus Colors, puedes pasarlos a un MaterialTheme:
MaterialTheme( colors = if (darkTheme) DarkColors else LightColors ) { // app content }
Usar colores del tema
Puedes recuperar los Colors proporcionados al elemento que admite compilación de MaterialTheme con MaterialTheme.colors.
Text( text = "Hello theming", color = MaterialTheme.colors.primary )
Color de contenido y superficie
Muchos componentes aceptan un par de color y color del contenido:
Surface( color = MaterialTheme.colors.surface, contentColor = contentColorFor(color), // ... ) { /* ... */ } TopAppBar( backgroundColor = MaterialTheme.colors.primarySurface, contentColor = contentColorFor(backgroundColor), // ... ) { /* ... */ }
Esto te permite no solo definir el color de un elemento componible, sino también proporcionar un color predeterminado para el contenido, es decir, los elementos componibles que contiene. Muchos elementos que admiten composición usan este color de contenido de forma predeterminada. Por ejemplo, Text basa su color en el color de contenido del elemento principal, y Icon usa ese color para establecer su tono.
El método contentColorFor() recupera el color "activado" adecuado para cualquier color del tema. Por ejemplo, si estableces un color de fondo primary en Surface, se usa esta función para establecer onPrimary como color de contenido.
Si configuras un color de fondo que no pertenece a ningún tema, también debes especificar un color de contenido adecuado. Usa LocalContentColor para recuperar el color de contenido preferido para el fondo actual, en una posición determinada de la jerarquía.
Versión alfa de contenido
Muchas veces quieres variar la forma en que destacas el contenido para comunicar la importancia y ofrecer una jerarquía visual. Las recomendaciones de legibilidad del texto de Material Design aconsejan emplear diferentes niveles de opacidad para transmitir diferentes niveles de importancia.
Jetpack Compose implementa esto con LocalContentAlpha. Puedes especificar una versión alfa de contenido para una jerarquía proporcionando un valor para este CompositionLocal. Los elementos componibles anidados pueden usar este valor para aplicar el tratamiento alfa a su contenido. Por ejemplo, Text y Icon de forma predeterminada utilizan la combinación de LocalContentColor ajustada para utilizar LocalContentAlpha. Material especifica algunos valores alfa estándar (high, medium y disabled) que están modelados por el 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 obtener más información sobre CompositionLocal, consulta Datos de alcance local con CompositionLocal.
ContentAlpha.high. La segunda línea contiene metadatos menos importantes y, por lo tanto, usa ContentAlpha.medium.Tema oscuro
En Compose, debes proporcionar diferentes conjuntos de Colors al elemento MaterialTheme que admite composición para implementar temas claros y oscuros:
@Composable fun MyTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { MaterialTheme( colors = if (darkTheme) DarkColors else LightColors, /*...*/ content = content ) }
En este ejemplo, MaterialTheme se une a su propia función que admite composición y que acepta un parámetro que especifica si se debe usar un tema oscuro o no. En este caso, la función consulta a la configuración de tema del dispositivo para obtener el valor predeterminado de darkTheme.
Puedes usar código como el siguiente para verificar si los objetos Colors actuales son claros u oscuros:
val isLightTheme = MaterialTheme.colors.isLight Icon( painterResource( id = if (isLightTheme) { R.drawable.ic_sun_24 } else { R.drawable.ic_moon_24 } ), contentDescription = "Theme" )
Superposiciones de elevación
En Material, las superficies en temas oscuros con mayor elevación reciben superposiciones de elevación, que aclaran su fondo. Cuanto más alta sea la elevación de una superficie (lo que la acerca más a una fuente de luz implícita), más clara se vuelve esa superficie.
El elemento Surface componible aplica estas superposiciones automáticamente cuando se usan colores oscuros, al igual que cualquier otro elemento componible de Material que use una superficie:
Surface( elevation = 2.dp, color = MaterialTheme.colors.surface, // color will be adjusted for elevation /*...*/ ) { /*...*/ }
surface como fondo. Dado que las tarjetas y la navegación inferior se encuentran en diferentes niveles de elevación por encima del fondo, tienen colores ligeramente diferentes: las tarjetas son más claras que el fondo y la navegación inferior es más clara que las tarjetas.Para situaciones personalizadas que no involucran un Surface, usa LocalElevationOverlay, un CompositionLocal que contiene el ElevationOverlay que usan los 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 inhabilitar las superposiciones de elevación, proporciona null en el punto elegido de una jerarquía componible:
MyTheme { CompositionLocalProvider(LocalElevationOverlay provides null) { // Content without elevation overlays } }
Acentuación de color limitada
Material recomienda aplicar acentuaciones de color limitadas para los temas oscuros, ya que prefiere el uso del color surface en lugar del color primary en la mayoría de los casos. Los elementos de Material que admiten composición, como TopAppBar y BottomNavigation, implementan este comportamiento de forma predeterminada.
Para situaciones personalizadas, usa la propiedad de extensión primarySurface:
Surface( // Switches between primary in light theme and surface in dark theme color = MaterialTheme.colors.primarySurface, /*...*/ ) { /*...*/ }
Tipografía
Material define un sistema de escritura que te recomienda usar una pequeña cantidad de estilos con nombres semánticos.
Compose implementa el sistema de escritura con las clases Typography, TextStyle y relacionadas con fuentes. El constructor de Typography ofrece valores predeterminados para cada estilo, de modo que puedes omitir cualquiera que no quieras 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, /*...*/) { /*...*/ }
Si quieres usar el mismo tipo de letra en todo el texto, especifica el parámetro defaultFontFamily y omite el fontFamily de cualquier elemento TextStyle:
val typography = Typography(defaultFontFamily = raleway) MaterialTheme(typography = typography, /*...*/) { /*...*/ }
Cómo usar estilos de texto
Se accede a los elementos TextStyle con MaterialTheme.typography. Recupera los elementos TextStyle de la siguiente manera:
Text( text = "Subtitle2 styled", style = MaterialTheme.typography.subtitle2 )

Forma
Material define un sistema de formas que te permite definir formas para componentes grandes, medianos y pequeños.

Compose implementa el sistema de formas con la clase Shapes, que te permite especificar un CornerBasedShape para cada categoría de tamaño:
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, /*...*/) { /*...*/ }
Muchos componentes usan estas formas de manera predeterminada. Por ejemplo, Button, TextField y FloatingActionButton se establecen de forma predeterminada en pequeño, AlertDialog se establece de forma predeterminada en mediano y ModalDrawer se establece de forma predeterminada en grande. Consulta la referencia del esquema de forma para obtener la asignación completa.
Cómo usar formas
Se accede a los elementos Shape con MaterialTheme.shapes. Recupera los elementos Shape con código como este:
Surface( shape = MaterialTheme.shapes.medium, /*...*/ ) { /*...*/ }
Estilos predeterminados
En Compose, no existe un concepto equivalente a los diseños predeterminados de las Vistas de Android. Puedes proporcionar una funcionalidad similar creando tus propias funciones de componibilidad overload que encapsulen componentes de Material. Por ejemplo, para crear un estilo de botón, une un botón en tu propia función de componibilidad, configura directamente los parámetros que deseas o necesitas modificar y expón otros como parámetros al elemento componible que los contiene.
@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 ) }
Superposiciones de temas
Puedes lograr el equivalente de las superposiciones de temas de las vistas de Android en Compose anidando elementos MaterialTheme componibles. Dado que MaterialTheme establece los colores, la tipografía y las formas de forma predeterminada en el valor del tema actual, todos los demás parámetros conservan sus valores predeterminados cuando un tema solo establece uno de esos parámetros.
Además, cuando migres pantallas basadas en View a Compose, presta atención a los usos del atributo android:theme. Es probable que necesites un nuevo MaterialTheme en esa parte del árbol de IU de Compose.
En este ejemplo, la pantalla de detalles usa un PinkTheme para la mayor parte de la pantalla y, luego, un BlueTheme para la sección relacionada. En la siguiente captura de pantalla y el siguiente código, se ilustra este concepto:
@Composable fun DetailsScreen(/* ... */) { PinkTheme { // other content RelatedSection() } } @Composable fun RelatedSection(/* ... */) { BlueTheme { // content } }
Estados de componentes
Los componentes de Material con los que se puede interactuar (con un clic o un botón de activación, etc.) pueden estar en diferentes estados visuales. Los estados incluyen habilitado, inhabilitado, presionado, etc.
Con frecuencia, los elementos que admiten composición tienen un parámetro enabled. Si se establece en false, se evita la interacción, y se cambian propiedades, como el color y la elevación, para transmitir, de forma visual, el estado del componente.
enabled = true (izquierda) y enabled = false (derecha).En la mayoría de los casos, puedes confiar en los parámetros predeterminados para valores como el color y la elevación. Si necesitas configurar valores que se usan en diferentes estados, hay clases y funciones de conveniencia disponibles. Considera el siguiente ejemplo de botón:
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 ) ) { /* ... */ }
enabled = true (izquierda) y enabled = false (derecha), con valores de color y elevación ajustados.Ecos
Los componentes de Material usan ondas para indicar que se está interactuando con ellos. Si usas MaterialTheme en tu jerarquía, se usa un Ripple como el Indication predeterminado dentro de modificadores como clickable y indication.
En la mayoría de los casos, puedes confiar en el objeto Ripple predeterminado. Si necesitas configurar su apariencia, puedes usar RippleTheme para cambiar propiedades como el color y el canal alfa.
Puedes extender RippleTheme y usar las funciones de utilidad defaultRippleColor y defaultRippleAlpha. Luego, puedes proporcionar tu tema de ondulación personalizado en tu jerarquía con 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 ) }
RippleConfiguration.Más información
Para obtener más información sobre los temas de Material en Compose, consulta los siguientes recursos adicionales.
Codelabs
Videos
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Sistemas de diseño personalizado en Compose
- Cómo migrar de Material 2 a Material 3 en Compose
- Accesibilidad en Compose