Material Design 3 en Compose

Jetpack Compose ofrece una implementación de Material Design 3, la siguiente evolución de Material Design. Material 3 incluye temas actualizados, componentes y funciones de personalización de Material You, como el color dinámico. Además, se diseñó para ser coherente con el nuevo estilo visual y la IU del sistema en Android 12 y versiones posteriores.

A continuación, demostramos la implementación de Material Design 3 con la app de ejemplo de Reply como ejemplo. La muestra de Reply se basa completamente en Material Design 3.

App de ejemplo de Reply con Material Design 3
Figura 1: App de ejemplo de Reply con Material Design 3

Dependencia

Para comenzar a usar Material 3 en tu app de Compose, agrega la dependencia de Compose Material 3 a tus archivos build.gradle:

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

Una vez que se agregue la dependencia, podrás comenzar a agregar sistemas de Material Design, incluidos color, tipografía y forma, a tus apps.

APIs experimentales

Algunas APIs de M3 se consideran experimentales. En esos casos, debes habilitar en el nivel de la función o del archivo con la anotación ExperimentalMaterial3Api:

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

Temas de Material

Un tema de M3 contiene los siguientes subsistemas: esquema de colores, tipografía y formas. Cuando personalizas estos valores, tus cambios se reflejan automáticamente en los componentes de M3 que usas para compilar tu app.

Subsistemas de Material Design: color, tipografía y formas
Figura 2: Subsistemas de Material Design: color, tipografía y formas

Jetpack Compose implementa esos conceptos con el elemento MaterialTheme de M3 que admite composición:

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

Para aplicar un tema al contenido de tu aplicación, define el esquema de colores, la tipografía y las formas específicas de tu app.

Esquema de colores

La base de un esquema de colores es el conjunto de cinco colores clave. Cada uno de estos colores se relaciona con una paleta tonal de 13 tonos, que usan los componentes de Material 3. Por ejemplo, este es el esquema de colores del Tema claro de Reply:

Esquema de colores claros de la app de ejemplo de Reply
Figura 3: Esquema de colores claros de la app de ejemplo de Reply

Obtén más información acerca de las funciones de color y el esquema de colores.

Generar esquemas de colores

Si bien puedes crear un ColorScheme personalizado de forma manual, suele ser más fácil generar uno con los colores de origen de tu marca. La herramienta Material Theme Builder te permite hacer esto y, de manera opcional, exportar el código de temas de Compose. Se generan los siguientes archivos:

  • Color.kt contiene los colores de tu tema con todos los roles definidos para los colores claro y oscuro.

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)
// ..
// ..

  • Theme.kt contiene una configuración para los esquemas de colores claros y oscuros, y el tema de la 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 admitir temas claros y oscuros, usa isSystemInDarkTheme(). Según la configuración del sistema, define el esquema de colores que usarás: oscuro o claro.

Esquemas de colores dinámicos

El color dinámico es la parte clave de Material You, en el que un algoritmo deriva colores personalizados del fondo de pantalla de un usuario para aplicarlos a sus apps y a la IU del sistema. Esta paleta de colores se usa como punto de partida para generar esquemas de colores claros y oscuros.

Temas dinámicos de la app de ejemplo de Reply desde el fondo de pantalla (izquierda) y temas predeterminados de la app (derecha)
Figura 4: Temas dinámicos de la app de ejemplo de Reply desde el fondo de pantalla (izquierda) y temas predeterminados de la app (derecha)

El color dinámico está disponible en Android 12 y versiones posteriores. Si el color dinámico está disponible, puedes configurar un ColorScheme dinámico. De lo contrario, debes recurrir a un ColorScheme claro u oscuro personalizado.

ColorScheme proporciona funciones de compilador para crear un esquema de colores claro u oscuro dinámico:

// 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 del color

Puedes acceder a los colores del tema de Material en tu app a través de MaterialTheme.colorScheme:

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

Cada función de color se puede usar en varios lugares según el estado, la importancia y el énfasis del componente.

  • El primario es el color de base, que se usa para componentes principales, como botones prominentes, estados activos y el tono de las superficies elevadas.
  • El color de clave secundaria se usa para componentes menos prominentes en la IU, como los chips de filtro, y expande la oportunidad para la expresión de color.
  • El color de clave terciaria se usa para derivar los roles de los acentos contrastantes que se pueden usar para equilibrar los colores primarios y secundarios o aumentar la atención a un elemento.

El diseño de la app de muestra de Reply usa el color del contenedor principal encima del contenedor principal para enfatizar el elemento seleccionado.

Contenedor principal y campos de texto con color del contenedor principal.
Figura 5: Contenedor principal y campos de texto con color del contenedor principal

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,
    )
}

Aquí puedes ver en el panel lateral de navegación de Reply cómo se usan los colores de contenedor secundarios y terciarios en contraste para crear énfasis y acento.

Combinación de contenedor terciario y contenedor terciario para el botón de acción flotante.
Figura 6: Combinación de contenedor terciario y contenedor terciario para el botón de acción flotante.

Tipografía

Material Design 3 define una escala de tipo, incluidos los estilos de texto que se adaptaron de Material Design 2. Los nombres y las agrupaciones se simplificaron a los siguientes: gráficos, encabezados, títulos, cuerpos y etiquetas, con tamaños grandes, medianos y pequeños para cada uno.

Escala de tipografía predeterminada para Material Design 3
Figura 7: Escala de tipografía predeterminada para Material Design 3
Versión 3 Tamaño de fuente o altura de línea predeterminados
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

Define la tipografía

Compose proporciona la clase Typography de M3, junto con las clases TextStyle existentes y relacionadas con la fuente, para modelar la escala de tipo de Material 3. El constructor Typography ofrece valores predeterminados para cada estilo, de modo que puedes omitir cualquier parámetro que no quieras 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
    ),
    // ..
)
// ..

Cuerpo grande, cuerpo mediano y etiqueta mediana para uso de tipografía diferente
Figura 8: Cuerpo grande, cuerpo mediano y etiqueta mediana para uso de tipografía diferente

Es probable que tu producto no necesite los 15 estilos predeterminados de la escala de tipo de Material Design. En este ejemplo, se eligen cinco tamaños para un conjunto reducido mientras se omite el resto.

Puedes personalizar la tipografía si cambias los valores predeterminados de TextStyle y las propiedades relacionadas con la fuente, como fontFamily y 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
),

Una vez que hayas definido tu Typography, pásalo a MaterialTheme de M3:

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

Cómo usar estilos de texto

Puedes recuperar la tipografía proporcionada al elemento componible MaterialTheme de M3 mediante MaterialTheme.typography:

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

Puedes leer más sobre los lineamientos de Material para aplicar tipografía.

Formas

Las superficies de Material se pueden mostrar en diferentes formas. Las formas dirigen la atención, identifican los componentes, comunican el estado y expresan la marca.

La escala de forma define el estilo de las esquinas del contenedor y ofrece un rango de redondez, desde cuadradas hasta completamente circulares.

Cómo definir formas

Compose proporciona la clase Shapes de M3 con parámetros expandidos para admitir formas nuevas de M3. La escala de forma de M3 se parece más a la escala de tipo, lo que habilita un rango expresivo de formas en toda la IU.

Las formas tienen diferentes tamaños:

  • Tamaño extrapequeño
  • Pequeño
  • Mediana
  • Grande
  • Tamaño extragrande

De forma predeterminada, cada forma tiene un valor predeterminado, pero se pueden anular de la siguiente manera:

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

Una vez que hayas definido tu Shapes, puedes pasarlo a MaterialTheme de M3:

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

Cómo usar formas

Puedes personalizar la escala de forma de todos los componentes en MaterialTheme o puedes hacerlo por componente.

Aplica formas medianas y grandes con valores predeterminados:

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

Forma media para la tarjeta y forma grande para el botón de acción flotante en la app de ejemplo de Reply.
Figura 9: Forma media para la tarjeta y forma grande para el botón de acción flotante en la app de ejemplo de Reply

Hay otras dos formas, RectangleShape y CircleShape, que forman parte de Compose. La forma rectangular no tiene un radio de borde, y la forma circular muestra los bordes con un círculo completo:

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

En los siguientes ejemplos, se muestran algunos de los componentes con valores de forma predeterminados aplicados:

Valores de formas predeterminados para todos los componentes de Material 3.
Figura 10: Valores de formas predeterminados para todos los componentes de Material 3

Puedes leer más sobre los lineamientos de Material para aplicar forma.

Énfasis

En M3, se hace énfasis en el uso de variaciones de color y sus combinaciones en el color. En M3, hay dos formas de agregar énfasis a tu IU:

  • Usando colores de superficie, variante de superficie y fondo junto con los colores de las variantes en la superficie del sistema de colores expandido de M3 Por ejemplo, Surface se puede usar con on-surface-variante y Surface-variante se puede usar on-surface para proporcionar diferentes niveles de énfasis.
Usar combinaciones de colores neutros para enfatizar el texto
Figura 11: Usar combinaciones de colores neutros para enfatizar
  • Puedes usar diferentes tamaños de fuente para el texto. Arriba, viste que puedes proporcionar pesos personalizados a nuestra escala de tipo para proporcionar un énfasis diferente.

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

Elevación

Material 3 representa la elevación, principalmente, usando superposiciones de colores tonales. Esta es una nueva forma de diferenciar los contenedores y las superficies entre sí (aumentar la elevación tonal utiliza un tono más prominente) además de las sombras.

Elevación tonal con elevación de sombra
Figura 12: Elevación tonal con elevación de sombrasE

Las superposiciones de elevación en Temas oscuros también cambiaron por las superposiciones de color tonales en Material 3. El color de superposición proviene del espacio de color primario.

Comparación entre la elevación de sombras y la elevación tonal en Material Design 3
Figura 13: La elevación de sombras en comparación con la elevación tonal en Material Design 3

La Surface de M3 (el elemento de copia de seguridad que admite composición detrás de la mayoría de los componentes de M3) admite la elevación tonal y de sombras:

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

Componentes de Material

Material Design incluye un amplio conjunto de componentes de Material (como botones, chips, tarjetas y barras de navegación), que ya siguen los temas de Material y te ayudan a crear apps atractivas de Material Design. Puedes comenzar a usar componentes con propiedades predeterminadas desde el primer momento.

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

M3 proporciona muchas versiones de los mismos componentes para usar en diferentes funciones según el énfasis y la atención.

Énfasis del botón desde el BAF, desde el botón principal hacia abajo hasta el botón de texto
Figura 14: Énfasis del botón desde el BAF, desde el botón de flecha hacia abajo hasta el botón de texto
  • Un botón de acción flotante extendido para la acción de énfasis más alto:

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

  • Un botón relleno para una acción de énfasis alto:

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

  • Un botón de texto para una acción de énfasis bajo:

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

Puedes leer más sobre los botones y otros componentes de Material. Material 3 ofrece una amplia variedad de paquetes de componentes, como botones, barras de apps y componentes de Navigation, que están diseñados específicamente para diferentes casos de uso y tamaños de pantalla.

Material también proporciona varios componentes de navegación que te ayudan a implementar la navegación, según los diferentes tamaños y estados de pantalla.

NavigationBar se usa para dispositivos compactos cuando deseas segmentar 5 destinos o menos:

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

NavigationRail se usa para tablets o teléfonos de tamaño pequeño a mediano en modo de paisaje. Proporciona ergonomía a los usuarios y mejora la experiencia del usuario en esos dispositivos.

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

Muestra de respuestas de BottomNavigationBar(Left) y NavigationRail(derecha)
Figura 15: Muestra de respuestas de BottomNavigationBar (izquierda) y NavigationRail (derecha)

Responde con ambos en el tema predeterminado a fin de proporcionar una experiencia del usuario envolvente para todos los tamaños de dispositivos.

NavigationDrawer se usa para tablets de tamaño mediano a grande en las que tienes suficiente espacio para mostrar detalles. Puedes usar PermanentNavigationDrawer o ModalNavigationDrawer junto con NavigationRail.

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

Muestra de respuestas del panel lateral de navegación permanente
Figura 16: Presentación de respuestas del panel lateral de navegación permanente

Las opciones de navegación mejoran la experiencia del usuario, la ergonomía y la accesibilidad. Puedes obtener más información sobre los componentes de navegación de Material en el codelab adaptable de Compose.

Cómo personalizar los temas de un componente

M3 fomenta la personalización y la flexibilidad. Todos los componentes tienen colores predeterminados aplicados, pero exponen APIs flexibles para personalizar sus colores si es necesario.

La mayoría de los componentes, como las tarjetas y los botones, proporcionan un objeto predeterminado que expone interfaces de color y elevación que se pueden modificar para personalizar el 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
}

Puedes leer más sobre cómo personalizar Material 3.

IU del sistema

Algunos aspectos de Material You provienen del nuevo estilo visual y de la IU del sistema en Android 12 y versiones posteriores. Dos áreas clave en las que hay cambios son las ondas y el sobredesplazamiento. No se requiere ninguna acción adicional para implementar estos cambios.

Onda

Ripple ahora usa un destello sutil para iluminar superficies cuando se presiona. Compose Material Ripple usa un RippleDrawable de plataforma de forma interna en Android, por lo que la onda de brillo está disponible en Android 12 y versiones posteriores para todos los componentes de Material.

Onda en M2 vs. M3
Figura 17: Onda en M2 en comparación con M3

Sobredesplazamiento

El sobredesplazamiento ahora usa un efecto de estiramiento en el borde de los contenedores de desplazamiento. El sobredesplazamiento estirado está activado de forma predeterminada en los elementos componibles de desplazamiento, por ejemplo, LazyColumn, LazyRow y LazyVerticalGrid, en Compose Foundation 1.1.0 y versiones posteriores, sin importar el nivel de API.

Sobredesplazamiento con efecto de estirar en el borde del contenedor
Figura 18: Sobredesplazamiento con efecto de estiramiento en el borde del contenedor

Accesibilidad

Los estándares de accesibilidad integrados en los componentes de Material están diseñados para proporcionar una base para el diseño de productos inclusivo. Comprender la accesibilidad de tu producto puede mejorar la usabilidad para todos los usuarios, incluidos aquellos con visión reducida, ceguera, discapacidades auditivas, discapacidades cognitivas, discapacidades motrices o discapacidades situacionales (como un brazo roto).

Accesibilidad a color

El color dinámico está diseñado para cumplir con los estándares de accesibilidad del contraste de color. El sistema de paletas tonales es fundamental para que se pueda acceder a cualquier esquema de colores de forma predeterminada.

El sistema de colores de Material proporciona valores de tono y medidas estándar que se pueden usar para cumplir con relaciones de contraste accesibles.

App de ejemplo de Reply: Palets tonales principales, secundarios y terciarios (de arriba abajo)
Figura 19: App de ejemplo de Reply: paletas tonales primaria, secundaria y terciaria (de arriba a abajo)

Todos los componentes y los temas dinámicos de Material ya usan las funciones de color anteriores de un conjunto de paletas de tonos, seleccionadas para cumplir con los requisitos de accesibilidad. Sin embargo, si personalizas los componentes, asegúrate de usar funciones de color adecuadas y evitar discrepancias.

Usa on-primary sobre el principal y on-primary-container encima del contenedor principal, y haz lo mismo para otros elementos destacados y colores neutros a fin de proporcionar un contraste accesible para el usuario.

El uso de un contenedor terciario por encima del principal le brinda al usuario un botón de contraste deficiente:

// ✅ 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 (izquierda) en comparación con contraste bajo (derecha)
Figura 20: Contraste suficiente (izquierda) en comparación con contraste deficiente (derecha)

Accesibilidad a la tipografía

La escala de tipo M3 actualiza la aceleración de tipos estáticos y los valores para ofrecer un framework simplificado pero dinámico de categorías de tamaño que se escalan en todos los dispositivos.

Por ejemplo, en M3, se pueden asignar diferentes valores a una pantalla pequeña según el contexto del dispositivo, como un teléfono o una tablet.

Pantallas grandes

Material proporciona orientación sobre diseños adaptables y dispositivos plegables para hacer que tus apps sean accesibles y mejorar la ergonomía de los usuarios que tienen dispositivos grandes.

Material proporciona diferentes tipos de navegación para ayudarte a brindar una mejor experiencia del usuario en dispositivos grandes.

Puedes obtener más información sobre los lineamientos para pantallas grandes de Android y consultar nuestra muestra de respuesta sobre diseño adaptable y accesible.

Más información

Para obtener más información sobre los temas de Material en Compose, consulta los siguientes recursos:

Apps de ejemplo

Documentos

Referencia de la API y código fuente

Videos