Temas de Material con Jetpack Compose

1. Antes de comenzar

Material Design es un sistema de diseño que crearon y respaldan diseñadores y desarrolladores de Google para crear experiencias digitales de alta calidad para Android, así como otras plataformas web y para dispositivos móviles. Proporciona lineamientos que te permitirán compilar la IU de tu app de manera legible, atractiva y coherente.

En este codelab, aprenderás sobre los Temas de Material, que te permiten usar Material Design en tu app, y te orientaremos para que personalices los colores, la tipografía y las formas. Puedes personalizar todo lo que quieras en tu app. También aprenderás a agregar una barra superior de la aplicación para mostrar el nombre y el ícono de la app.

Requisitos previos

  • Conocer el lenguaje Kotlin, incluidas la sintaxis, las funciones y las variables
  • Poder compilar diseños en Compose, incluidas filas y columnas con padding
  • Ser capaz de crear listas simples en Compose

Qué aprenderás

  • Cómo aplicar Temas de Material a una app de Compose
  • Cómo agregar una paleta de colores personalizada a tu app
  • Cómo agregar fuentes personalizadas a tu app
  • Cómo agregar formas personalizadas a los elementos de tu app
  • Cómo agregar una barra superior a la app

Qué compilarás

  • Compilarás una app atractiva que incorpore las prácticas recomendadas de Material Design.

Requisitos

  • La versión más reciente de Android Studio
  • Una conexión a Internet para descargar el código de partida y las fuentes

2. Descripción general de la app

En este codelab, crearás Woof, una app que muestra una lista de perros y usa Material Design para crear una experiencia de app atractiva.

92eca92f64b029cf.png

En este codelab, te mostraremos lo que se puede lograr con Temas de Material. Usa este codelab para obtener ideas de uso de Temas de Material y mejorar el aspecto de las apps que crees en el futuro.

Paleta de colores

A continuación, se muestran las paletas de colores para los temas claros y oscuros que crearemos.

Esta imagen tiene el esquema de colores claros de la app de Woof.

Esta imagen tiene el esquema de colores oscuros de la app de Woof.

Esta es la app final con los temas claro y oscuro.

Tema claro

Tema oscuro

Tipografía

A continuación, se muestran los estilos de tipo que usarás en la app.

8ea685b3871d5ffc.png

Archivo de tema

El archivo Theme.kt contiene toda la información sobre el tema de la app, que se define por el color, la tipografía y la forma. Es un archivo importante que debes conocer. Dentro del archivo, se encuentra el elemento WoofTheme() componible, que establece los colores, la tipografía y las formas de la app.

@Composable
fun WoofTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    // Dynamic color is available on Android 12+
    dynamicColor: Boolean = false,
    content: @Composable () -> Unit
) {
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            val context = LocalContext.current
            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
        }

        darkTheme -> DarkColors
        else -> LightColors
    }
    val view = LocalView.current
    if (!view.isInEditMode) {
        SideEffect {
            setUpEdgeToEdge(view, darkTheme)
        }
    }

    MaterialTheme(
        colorScheme = colorScheme,
        shapes = Shapes,
        typography = Typography,
        content = content
    )
}

/**
 * Sets up edge-to-edge for the window of this [view]. The system icon colors are set to either
 * light or dark depending on whether the [darkTheme] is enabled or not.
 */
private fun setUpEdgeToEdge(view: View, darkTheme: Boolean) {
    val window = (view.context as Activity).window
    WindowCompat.setDecorFitsSystemWindows(window, false)
    window.statusBarColor = Color.Transparent.toArgb()
    val navigationBarColor = when {
        Build.VERSION.SDK_INT >= 29 -> Color.Transparent.toArgb()
        Build.VERSION.SDK_INT >= 26 -> Color(0xFF, 0xFF, 0xFF, 0x63).toArgb()
        // Min sdk version for this app is 24, this block is for SDK versions 24 and 25
        else -> Color(0x00, 0x00, 0x00, 0x50).toArgb()
    }
    window.navigationBarColor = navigationBarColor
    val controller = WindowCompat.getInsetsController(window, view)
    controller.isAppearanceLightStatusBars = !darkTheme
    controller.isAppearanceLightNavigationBars = !darkTheme
}

En MainActivity.kt, se agrega WoofTheme() para proporcionar los Temas de Material a toda la app.

class MainActivity : ComponentActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContent {
           WoofTheme {
               Surface(
                   modifier = Modifier.fillMaxSize()
               ) {
                   WoofApp()
               }
           }
       }
   }
}

Echa un vistazo a WoofPreview(). Se agrega WoofTheme() para proporcionar los Temas de Material que ves en WoofPreview().

@Preview
@Composable
fun WoofPreview() {
    WoofTheme(darkTheme = false) {
        WoofApp()
    }
}

3. Obtén el código de partida

Para comenzar, descarga el código de partida:

Descargar ZIP

Como alternativa, puedes clonar el repositorio de GitHub para el código:

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-woof.git
$ cd basic-android-kotlin-compose-training-woof
$ git checkout starter

Puedes explorar el código en el repositorio de GitHub de Woof app.

Explora el código de partida

  1. Abre el código de partida en Android Studio.
  2. Abre com.example.woof > data > Dog.kt. Este contiene el objeto Dog data class que se usará para representar la foto, el nombre, la edad y los pasatiempos del perro. También contiene una lista de perros y la información que usarás como datos en tu app.
  3. Abre res > drawable. Este contiene todos los recursos de imagen que necesitas para el proyecto, incluidos el ícono de la app, los demás íconos y las imágenes de los perros.
  4. Abre res > values > strings.xml. Allí están las cadenas que usarás en esta app, lo que incluye el nombre de la app, los nombres de los perros, las descripciones y mucho más.
  5. Abre MainActivity.kt. Este contiene el código para crear una lista sencilla que muestre la foto, el nombre y la edad de un perro.
  6. WoofApp() contiene una LazyColumn que muestra los DogItem.
  7. DogItem() contiene una Row que muestra una foto y la información del perro.
  8. DogIcon() muestra una foto del perro.
  9. DogInformation() muestra el nombre y la edad del perro.
  10. WoofPreview() te permite obtener una vista previa de la app en el panel Design.

Asegúrate de que el emulador o dispositivo tenga activado el Tema claro

Aunque en este codelab trabajarás con temas claros y oscuros, la mayor parte está en el Tema claro. Antes de comenzar, asegúrate de que tu dispositivo o emulador tenga activado el Tema claro.

Para ver tu app en Tema claro, en el emulador o dispositivo físico, haz lo siguiente:

  1. Ve a la app de Configuración.
  2. Busca el Tema oscuro y haz clic en él.
  3. Si el Tema oscuro está activado, desactívalo.

Ejecuta el código de partida para ver con qué comenzarás: es una lista que muestra perros con sus fotos, nombres y edades. Funciona, pero no se ve bien, así que vamos a corregir eso.

6d253ae50c63014d.png

4. Agrega color

Lo primero que modificarás en la app de Woof es el esquema de colores.

Un esquema de colores es la combinación de colores que usa la app. Las distintas combinaciones de colores evocan diferentes estados de ánimo, lo que influye en cómo se sienten las personas cuando usan tu app.

En el sistema Android, el color se representa con un valor hexadecimal (hex). Un código de color hexadecimal comienza con un carácter numeral (#), seguido de seis letras o números que representan los componentes rojo, verde y azul (RGB) de ese color. Las primeras dos letras/números hacen referencia al rojo, las dos siguientes hacen referencia al verde y las dos últimas al azul.

Esto muestra los números hexadecimales que se utilizan para crear los colores.

Un color también puede incluir un valor alfa (letras o números), que representa la transparencia del color: #00 es 0% de opacidad (completamente transparente) y #FF es 100% de opacidad (completamente opaco). Cuando se incluye, el valor alfa corresponde a los dos primeros caracteres del código de color hexadecimal que aparece después del numeral (#). Si no se incluye un valor alfa, se supone que es #FF, que es un 100% de opacidad (completamente opaco).

A continuación, se muestran algunos colores de ejemplo y sus valores hexadecimales.

2753d8cdd396c449.png

Usa Material Theme Builder para crear un esquema de colores

Para crear un esquema de colores personalizados para nuestra app, usaremos Material Theme Builder.

  1. Haz clic en este vínculo para ir a Material Theme Builder.
  2. En el panel izquierdo, verás los colores principales. Haz clic en Primary:

Se muestran cuatro colores principales en Material Theme Builder

  1. Se abrirá el selector de color de HCT

Este es el selector de color de HCT para elegir un color personalizado en Material Theme Builder.

  1. Para crear el esquema de colores que se muestra en las capturas de pantalla de la app, cambiarás el color primario en este selector. En el cuadro de texto, reemplaza el texto actual por #006C4C. Esto hará que el color primario de la app sea verde.

Se muestra el selector de color de HCT establecido en verde

Observa cómo se actualizan las apps en la pantalla para adoptar un esquema de colores verde.

Se muestran las apps de Material Theme Builder que reaccionan al cambio de color del selector de color de HCT.

  1. Desplázate hacia abajo en la página y verás el esquema de colores completo de los temas oscuros y claros que se generaron a partir del color que ingresaste.

Esquema claro de Material Theme Builder

Esquema oscuro generado por Material Theme Builder

Quizás te preguntes qué son todos estos roles y cómo se utilizan. Estos son algunos de los principales:

  • Los colores primary (primarios) se usan para los componentes clave de la IU.
  • Los colores secondary (secundarios) se usan para los componentes menos destacados de la IU.
  • Los colores tertiary (terciarios) se usan para contrastar los acentos que pueden utilizarse para equilibrar los colores primarios y secundarios, o dirigir la atención hacia un elemento, como un campo de entrada.
  • Los elementos de color on aparecen arriba de otros colores en la paleta y se aplican principalmente al texto, la iconografía y los trazos. En la paleta de colores, tenemos un color onSurface, que aparece en la parte superior del color surface, y un color onPrimary, que aparece en la parte superior del color primary.

Tener estas ranuras conduce a un sistema de diseño cohesivo, en el que los componentes relacionados se colorean de manera similar.

Suficiente teoría sobre los colores: es hora de agregar esta hermosa paleta de colores a la app.

Agrega una paleta de colores al tema

En la página de Material Theme Builder, puedes hacer clic en el botón Export para descargar un archivo Color.kt y un archivo Theme.kt con el tema personalizado que creaste en Theme Builder.

Esto funcionará para agregar el tema personalizado que creemos a tu app. Sin embargo, como el archivo Theme.kt generado no incluye el código de color dinámico que veremos más adelante en el codelab, copia los archivos.

  1. Abre el archivo Color.kt y reemplaza el contenido con el siguiente código para copiarlo en el nuevo esquema de colores.
package com.example.woof.ui.theme

import androidx.compose.ui.graphics.Color

val md_theme_light_primary = Color(0xFF006C4C)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFF89F8C7)
val md_theme_light_onPrimaryContainer = Color(0xFF002114)
val md_theme_light_secondary = Color(0xFF4D6357)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFCFE9D9)
val md_theme_light_onSecondaryContainer = Color(0xFF092016)
val md_theme_light_tertiary = Color(0xFF3D6373)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFC1E8FB)
val md_theme_light_onTertiaryContainer = Color(0xFF001F29)
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(0xFFFBFDF9)
val md_theme_light_onBackground = Color(0xFF191C1A)
val md_theme_light_surface = Color(0xFFFBFDF9)
val md_theme_light_onSurface = Color(0xFF191C1A)
val md_theme_light_surfaceVariant = Color(0xFFDBE5DD)
val md_theme_light_onSurfaceVariant = Color(0xFF404943)
val md_theme_light_outline = Color(0xFF707973)
val md_theme_light_inverseOnSurface = Color(0xFFEFF1ED)
val md_theme_light_inverseSurface = Color(0xFF2E312F)
val md_theme_light_inversePrimary = Color(0xFF6CDBAC)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF006C4C)
val md_theme_light_outlineVariant = Color(0xFFBFC9C2)
val md_theme_light_scrim = Color(0xFF000000)

val md_theme_dark_primary = Color(0xFF6CDBAC)
val md_theme_dark_onPrimary = Color(0xFF003826)
val md_theme_dark_primaryContainer = Color(0xFF005138)
val md_theme_dark_onPrimaryContainer = Color(0xFF89F8C7)
val md_theme_dark_secondary = Color(0xFFB3CCBE)
val md_theme_dark_onSecondary = Color(0xFF1F352A)
val md_theme_dark_secondaryContainer = Color(0xFF354B40)
val md_theme_dark_onSecondaryContainer = Color(0xFFCFE9D9)
val md_theme_dark_tertiary = Color(0xFFA5CCDF)
val md_theme_dark_onTertiary = Color(0xFF073543)
val md_theme_dark_tertiaryContainer = Color(0xFF244C5B)
val md_theme_dark_onTertiaryContainer = Color(0xFFC1E8FB)
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(0xFF191C1A)
val md_theme_dark_onBackground = Color(0xFFE1E3DF)
val md_theme_dark_surface = Color(0xFF191C1A)
val md_theme_dark_onSurface = Color(0xFFE1E3DF)
val md_theme_dark_surfaceVariant = Color(0xFF404943)
val md_theme_dark_onSurfaceVariant = Color(0xFFBFC9C2)
val md_theme_dark_outline = Color(0xFF8A938C)
val md_theme_dark_inverseOnSurface = Color(0xFF191C1A)
val md_theme_dark_inverseSurface = Color(0xFFE1E3DF)
val md_theme_dark_inversePrimary = Color(0xFF006C4C)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFF6CDBAC)
val md_theme_dark_outlineVariant = Color(0xFF404943)
val md_theme_dark_scrim = Color(0xFF000000)
  1. Abre el archivo Theme.kt y reemplaza el contenido con el siguiente código para agregar los nuevos colores al tema.
package com.example.woof.ui.theme

import android.app.Activity
import android.os.Build
import android.view.View
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat

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 WoofTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    // Dynamic color is available on Android 12+
    dynamicColor: Boolean = false,
    content: @Composable () -> Unit
) {
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            val context = LocalContext.current
            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
        }

        darkTheme -> DarkColors
        else -> LightColors
    }
    val view = LocalView.current
    if (!view.isInEditMode) {
        SideEffect {
            setUpEdgeToEdge(view, darkTheme)
        }
    }

    MaterialTheme(
        colorScheme = colorScheme,
        shapes = Shapes,
        typography = Typography,
        content = content
    )
}

/**
 * Sets up edge-to-edge for the window of this [view]. The system icon colors are set to either
 * light or dark depending on whether the [darkTheme] is enabled or not.
 */
private fun setUpEdgeToEdge(view: View, darkTheme: Boolean) {
    val window = (view.context as Activity).window
    WindowCompat.setDecorFitsSystemWindows(window, false)
    window.statusBarColor = Color.Transparent.toArgb()
    val navigationBarColor = when {
        Build.VERSION.SDK_INT >= 29 -> Color.Transparent.toArgb()
        Build.VERSION.SDK_INT >= 26 -> Color(0xFF, 0xFF, 0xFF, 0x63).toArgb()
        // Min sdk version for this app is 24, this block is for SDK versions 24 and 25
        else -> Color(0x00, 0x00, 0x00, 0x50).toArgb()
    }
    window.navigationBarColor = navigationBarColor
    val controller = WindowCompat.getInsetsController(window, view)
    controller.isAppearanceLightStatusBars = !darkTheme
    controller.isAppearanceLightNavigationBars = !darkTheme
}

En WoofTheme(), colorScheme val usa una sentencia when.

  • Si dynamicColor es "true" y la versión de compilación es S o posterior, comprueba si el dispositivo está en darkTheme o no.
  • Si está en el tema oscuro, se establecerá colorScheme como dynamicDarkColorScheme.
  • Si no está en el tema oscuro, se establecerá en dynamicLightColorScheme.
  • Si la app no usa dynamicColorScheme, verifica si está en darkTheme. Si es así, colorScheme se configurará como DarkColors.
  • Si ninguna de esas condiciones es "true", colorScheme se establecerá como LightColors.

El archivo Theme.kt copiado tiene dynamicColor configurado como "false" y los dispositivos con los que estamos trabajando están en modo claro, por lo que colorScheme se establecerá como LightColors.

val colorScheme = when {
       dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
           val context = LocalContext.current
           if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
       }

       darkTheme -> DarkColors
       else -> LightColors
   }
  1. Vuelve a ejecutar tu app. Observa que la barra de la app cambió automáticamente de color.

b48b3fa2ecec9b86.png

Asignación de colores

Los componentes de Material se asignan automáticamente a ranuras de color. Otros componentes clave en la IU, como los botones de acción flotantes, también tienen color primario de manera predeterminada. Esto significa que no necesitas asignar de forma explícita un color a un componente; se asigna automáticamente a una ranura de color cuando configuras el tema de color en tu app. Puedes anular esto si configuras de manera explícita un color en el código. Obtén más información sobre los roles de color aquí.

En esta sección, uniremos la Row que contiene DogIcon() y DogInformation() con una Card para diferenciar los colores del elemento de lista con el fondo.

  1. En la función de componibilidad DogItem(), une Row() con Card().
Card() {
   Row(
       modifier = modifier
           .fillMaxWidth()
           .padding(dimensionResource(id = R.dimen.padding_small))
   ) {
       DogIcon(dog.imageResourceId)
       DogInformation(dog.name, dog.age)
   }
}
  1. Como ahora Card es el primer elemento componible secundario en DogItem(), pasa el modificador de DogItem() a Card y actualiza el modificador de Row a una nueva instancia de Modifier.
Card(modifier = modifier) {
   Row(
       modifier = Modifier
           .fillMaxWidth()
           .padding(dimensionResource(id = R.dimen.padding_small))
   ) {
       DogIcon(dog.imageResourceId)
       DogInformation(dog.name, dog.age)
   }
}
  1. Echa un vistazo a WoofPreview(). Los elementos de lista ahora cambiaron automáticamente de color debido a los elementos Card componibles. Los colores se ven muy bien, pero no hay espacio entre los elementos de lista.

6d49372a1ef49bc7.png

Archivo dimens

Así como usas strings.xml para almacenar las cadenas en tu app, te recomendamos que uses un archivo llamado dimens.xml para almacenar valores de dimensión. Esto es útil para que no uses valores hard-coded y, si lo necesitas, puedas cambiarlos en un solo lugar.

Ve a app > res > values > dimens.xml y echa un vistazo al archivo. Almacena valores de dimensión para padding_small, padding_medium y image_size. Estas dimensiones se usarán en toda la app.

<resources>
   <dimen name="padding_small">8dp</dimen>
   <dimen name="padding_medium">16dp</dimen>
   <dimen name="image_size">64dp</dimen>
</resources>

Para agregar un valor desde el archivo dimens.xml, este es el formato correcto:

Muestra cómo dar el formato correcto para agregar valores del recurso de dimensión

Por ejemplo, para agregar padding_small, debes pasar dimensionResource(id = R.dimen.padding_small).

  1. En WoofApp(), agrega un modifier con padding_small en la llamada a DogItem().
@Composable
fun WoofApp() {
    Scaffold { it ->
        LazyColumn(contentPadding = it) {
            items(dogs) {
                DogItem(
                    dog = it,
                    modifier = Modifier.padding(dimensionResource(R.dimen.padding_small))
                )
            }
        }
    }
}

En WoofPreview(), ahora hay más definición entre los elementos de lista.

c54f870f121fe02.png

Tema oscuro

En el sistema Android, está la opción de cambiar tu dispositivo a un Tema oscuro. Un tema oscuro utiliza colores más oscuros y discretos. Además:

  • Puede reducir significativamente el consumo de energía (según la tecnología de pantalla del dispositivo).
  • Mejora la visibilidad para los usuarios con visión reducida y los usuarios sensibles a la luz intensa.
  • Facilita el uso de los dispositivos en entornos con poca luz.

La app puede aceptar Forzar oscuro, lo que significa que el sistema implementará el Tema oscuro por ti. Sin embargo, si implementas el Tema oscuro, los usuarios tendrán una mejor experiencia, lo que a su vez te permitirá tener el control total sobre el tema de la app.

Si eliges un Tema oscuro propio, es importante que tengas en cuenta que los colores para ese tema deben cumplir con los estándares de contraste de accesibilidad. Los temas oscuros usan un color de superficie oscuro con acentuación de color limitada.

Visualiza el tema oscuro en la vista previa

Ya agregaste los colores para el tema oscuro en el paso anterior. Para ver el tema oscuro en acción, agregarás otro elemento componible de vista previa a MainActivity.kt. De esa manera, cuando cambies el diseño de la IU en tu código, podrás ver el aspecto de los temas claro y oscuro simultáneamente en la vista previa.

  1. En WoofPreview(), crea una nueva función llamada WoofDarkThemePreview() y anótala con @Preview y @Composable.
@Preview
@Composable
fun WoofDarkThemePreview() {

}
  1. Dentro de DarkThemePreview(), agrega WoofTheme(). Si no incluyes WoofTheme(), no verás ninguno de los estilos que agregamos en la app. Establece el parámetro darkTheme en verdadero.
@Preview
@Composable
fun WoofDarkThemePreview() {
   WoofTheme(darkTheme = true) {

   }
}
  1. Llama a WoofApp() en WoofTheme().
@Preview
@Composable
fun WoofDarkThemePreview() {
   WoofTheme(darkTheme = true) {
       WoofApp()
   }
}

Ahora, desplázate hacia abajo en el panel Design para ver la app en tema oscuro, incluido el fondo más oscuro del elemento de la app o la lista y el texto más claro. Compara las diferencias entre los temas oscuro y claro.

Tema oscuro

Tema claro

Visualiza el Tema oscuro en tu dispositivo o emulador

Para ver tu app en Tema oscuro en el emulador o dispositivo físico, haz lo siguiente:

  1. Ve a la app de Configuración.
  2. Busca el Tema oscuro y haz clic en él.
  3. Activa el Tema oscuro.
  4. Vuelve a abrir la app de Woof y tendrá activado el Tema oscuro.

bc31a94207265b08.png

Este codelab se enfoca más en el Tema claro, por lo que, antes de continuar con la app, debes desactivar el Tema oscuro.

  1. Ve a la app de Configuración.
  2. Selecciona Pantalla.
  3. Desactiva Tema oscuro.

Compara cómo se veía la app al comienzo de la sección en comparación con su aspecto actual. Los elementos y el texto de la lista están más definidos y el esquema de colores es más atractivo a nivel visual.

Sin color

Con color (Tema claro)

Con color (Tema oscuro)

Color dinámico

Material 3 se enfoca principalmente en la personalización del usuario: una nueva función de Material 3 es el color dinámico, que crea un tema para tu app en función del fondo de pantalla del usuario. De este modo, si al usuario le encanta el color azul y tiene un fondo azul en el teléfono, la app de Woof también será azul para reflejar esa preferencia. Los temas dinámicos solo están disponibles en algunos dispositivos que ejecutan Android 12 y versiones posteriores.

Un tema personalizado se puede usar para apps que tienen colores de marca fuertes y que también se debe implementar en el caso de los dispositivos que no admiten temas dinámicos, de modo que tu app siga teniendo un tema.

  1. Para habilitar el color dinámico, abre Theme.kt, ve al elemento WoofTheme() componible y establece el parámetro dynamicColor en true.
@Composable
fun WoofTheme(
   darkTheme: Boolean = isSystemInDarkTheme(),
   dynamicColor: Boolean = true,
   content: @Composable () -> Unit
)
  1. Para cambiar el fondo de un dispositivo o emulador, ve a Configuración y busca Fondo de pantalla.
  2. Cambia el fondo de pantalla a un color o un conjunto de colores.
  3. Vuelve a ejecutar la app para ver el tema dinámico (ten en cuenta que tu dispositivo o emulador debe ejecutar Android 12 o versiones posteriores para ver el color dinámico). Puedes experimentar con diferentes fondos de pantalla.

710bd13f6b189dc5.png

  1. Este codelab se enfoca en los temas personalizados, por lo que debes inhabilitar dynamicColor antes de continuar.
@Composable
fun WoofTheme(
   darkTheme: Boolean = isSystemInDarkTheme(),
   dynamicColor: Boolean = false,
   content: @Composable () -> Unit
)

5. Agrega formas

Aplicar una forma puede cambiar mucho el aspecto de un elemento componible. Las formas dirigen la atención, identifican los componentes, comunican el estado y expresan la marca.

Muchas formas se definen con RoundedCornerShape, que describe un rectángulo con esquinas redondeadas. Los números que se pasen determinarán la redondez de las esquinas. Si se usa RoundedCornerShape(0.dp), el rectángulo no tendrá esquinas redondeadas. Si se usa RoundedCornerShape(50.dp), las esquinas serán completamente circulares.

0.dp

25.dp

50.dp

Elemento de lista de Woof con forma

Elemento de lista de Woof con forma

Elemento de lista de Woof con forma

También puedes personalizar más las formas agregando diferentes porcentajes de redondeo en cada esquina. ¡Es muy divertido jugar con las formas!

Parte superior izquierda: 50.dp
Parte inferior izquierda: 25.dp
Parte superior derecha: 0.dp
Parte inferior derecha: 15.dp

Parte superior izquierda: 15.dp
Parte inferior izquierda: 50.dp
Parte superior derecha: 50.dp
Parte inferior derecha: 15.dp

Parte superior izquierda: 0.dp
Parte inferior izquierda: 50.dp
Parte superior derecha: 0.dp
Parte inferior derecha: 50.dp

Elemento de lista de Woof con forma

Elemento de lista de Woof con forma

Elemento de lista de Woof con forma

El archivo Shape.kt se usa para definir formas de componentes en Compose. Existen tres tipos de componentes: pequeños, medianos y grandes. En esta sección, modificarás el componente Card, que se define como el tamaño medium. Los componentes se agrupan en categorías de formas según su tamaño.

En esta sección, darás forma de círculo a la imagen del perro y modificarás la forma del elemento de lista.

Dale forma de círculo a la imagen del perro

  1. Abre el archivo Shape.kt y observa que el parámetro pequeño está configurado en RoundedCornerShape(50.dp). Se usará para dar forma de círculo a la imagen.
val Shapes = Shapes(
   small = RoundedCornerShape(50.dp),
)
  1. Abre MainActivity.kt. En DogIcon(), agrega un atributo clip al modifier de Image. Esto recortará la imagen para crear una forma. Pasa MaterialTheme.shapes.small.
import androidx.compose.ui.draw.clip

@Composable
fun DogIcon(
   @DrawableRes dogIcon: Int,
   modifier: Modifier = Modifier
) {
   Image(
       modifier = modifier
           .size(dimensionResource(id = R.dimen.image_size))
           .padding(dimensionResource(id = R.dimen.padding_small))
           .clip(MaterialTheme.shapes.small),

Cuando observes WoofPreview(), notarás que los íconos de los perros son circulares. Sin embargo, algunas fotos están cortadas a los lados y no aparecen como completamente circulares.

1d4d1e5eaaddf71e.png

  1. Para hacer que todas las fotos sean circulares, agrega un atributo ContentScale y un Crop; la imagen se recortará para ajustarse. Ten en cuenta que contentScale es un atributo de Image y no forma parte de modifier.
import androidx.compose.ui.layout.ContentScale

@Composable
fun DogIcon(
   @DrawableRes dogIcon: Int,
   modifier: Modifier = Modifier
) {
   Image(
       modifier = modifier
           .size(dimensionResource(id = R.dimen.image_size))
           .padding(dimensionResource(id = R.dimen.padding_small))
           .clip(MaterialTheme.shapes.small),
       contentScale = ContentScale.Crop,

Este es el elemento componible DogIcon() completo.

@Composable
fun DogIcon(
    @DrawableRes dogIcon: Int,
    modifier: Modifier = Modifier
) {
    Image(
        modifier = modifier
            .size(dimensionResource(R.dimen.image_size))
            .padding(dimensionResource(R.dimen.padding_small))
            .clip(MaterialTheme.shapes.small),
        contentScale = ContentScale.Crop,
        painter = painterResource(dogIcon),

        // Content Description is not needed here - image is decorative, and setting a null content
        // description allows accessibility services to skip this element during navigation.

        contentDescription = null
    )
}

Ahora, en WoofPreview(), los íconos son circulares.

fc93106990f5e161.png

Agrega una forma al elemento de lista

En esta sección, agregarás una forma al elemento de lista. Este elemento ya se muestra a través de Card. Una Card es una superficie que puede contener un solo elemento componible y opciones de decoración. La decoración se puede agregar a través del borde, la forma y otras opciones. En esta sección, usarás Card para agregar una forma al elemento de lista.

El elemento de lista de Woof con dimensiones de forma agregadas

  1. Abre el archivo Shape.kt. Card es un componente medio, por lo que agregas el parámetro medio del objeto Shapes. En el caso de esta app, son las esquinas superior derecha e inferior izquierda del elemento de lista, pero no las hagas completamente circulares. Para lograrlo, pasa 16.dp al atributo medium.
medium = RoundedCornerShape(bottomStart = 16.dp, topEnd = 16.dp)

Como un objeto Card, de forma predeterminada, ya usa la forma media, no tienes que establecerla explícitamente. Consulta la vista previa para ver Card con la nueva forma.

Vista previa de Woof con tarjetas con formas

Si regresas al archivo Theme.kt en WoofTheme() y observas el MaterialTheme(), verás que el atributo shapes está establecido en el Shapes val que acabas de actualizar.

MaterialTheme(
   colors = colors,
   typography = Typography,
   shapes = Shapes,
   content = content
)

A continuación, se muestra una vista en paralelo de los elementos de lista antes y después de dar la forma. Observa qué tan visualmente atractiva es gracias a las formas que le agregamos.

Sin formas

Con formas

6. Agrega tipografía

La escala de tipo de Material Design

Una escala de tipos es una selección de estilos de fuente que pueden usarse en una app para garantizar un estilo flexible y coherente. La escala de tipos de Material Design incluye quince estilos de fuentes compatibles con el sistema de tipos. Los nombres y las agrupaciones se simplificaron a los siguientes: pantalla, encabezado, título, cuerpo y etiqueta, con tamaños grandes, medianos y pequeños para cada categoría. Solo necesitas usar estas opciones si quieres personalizar la app. Si no sabes qué configurar para cada categoría de escala de tipos, ten en cuenta que puedes usar una escala de tipografía predeterminada.

999a161dcd9b0ec4.png

La escala de tipos contiene categorías de texto reutilizables, cada una con una aplicación y un significado intencionados.

Pantalla

Como el texto más grande en pantalla, los estilos de pantalla se reservan para texto o números importantes y breves. Funcionan mejor en pantallas grandes.

Encabezado

Los encabezados son más adecuados para textos breves y de alto énfasis en pantallas más pequeñas. Estos estilos pueden ser útiles para marcar pasajes de texto principales o regiones de contenido importantes.

Título

Los títulos son más pequeños que los estilos de encabezados y deben usarse para el texto de énfasis medio que se mantiene relativamente breve.

Cuerpo

Los estilos de cuerpo se usan para pasajes de texto más largos en la app.

Etiqueta

Los estilos de etiqueta son estilos más pequeños y funcionales que se usan, por ejemplo, para texto dentro de componentes o para texto muy pequeño en el cuerpo del contenido, como los subtítulos.

Fuentes

La plataforma de Android ofrece una variedad de fuentes, pero te recomendamos que personalices tu app con una fuente que no se proporcione de forma predeterminada. Las fuentes personalizadas pueden agregar personalidad y usarse para el desarrollo de la marca.

En esta sección, agregarás las fuentes personalizadas llamadas Abril Fatface, Montserrat Bold y Montserrat Regular. Usarás los encabezados displayLarge y displayMedium, y el texto bodyLarge del sistema de Material Type, y los agregarás al texto en tu app.

Crea un directorio de recursos de Android para fuentes.

Antes de agregar fuentes a tu app, deberás crear un directorio de fuentes.

  1. En la vista de proyectos de Android Studio, haz clic con el botón derecho en la carpeta res.
  2. Selecciona New > Android Resource Directory.

En esta imagen, se muestra cómo navegar por la estructura de archivos hasta el Directorio de recursos de Android.

  1. Asigna el nombre font al directorio, establece el tipo de recurso en font y haz clic en OK.

En esta imagen, se muestra cómo agregar un directorio de fuentes con el nuevo directorio de recursos.

  1. Abre el nuevo directorio de recursos de fuentes en res > font.

Descarga fuentes personalizadas

Como utilizas fuentes no proporcionadas por la plataforma de Android, debes descargarlas.

  1. Ve a https://fonts.google.com/.
  2. Busca Montserrat y haz clic en Download family.
  3. Descomprime el archivo ZIP.
  4. Abre la carpeta Montserrat que descargaste. En la carpeta static, busca Montserrat-Bold.ttf y Montserrat-Regular.ttf (ttf significa TrueType y es el formato de los archivos de fuentes). Selecciona ambas fuentes y arrástralas al directorio de recursos de fuentes de tu proyecto en Android Studio.

Esta imagen muestra el contenido de la carpeta estática de las fuentes de Montserrat.

  1. En la carpeta de fuentes, cambia el nombre de Montserrat-Bold.ttf a montserrat_bold.ttf y el de Montserrat-Regular.ttf a montserrat_regular.ttf.
  2. Busca Abril Fatface y haz clic en Download family.
  3. Abre la carpeta Abril_Fatface que descargaste. Selecciona AbrilFatface-Regular.ttf y arrástrala al directorio de recursos de fuentes.
  4. En la carpeta de fuentes, cambia el nombre de Abril_Fatface_Regular.ttf a abril_fatface_regular.ttf.

Así se verá el directorio de recursos de fuentes en tu proyecto con los tres archivos de fuentes personalizados:

En esta imagen, se muestra cómo se agregan los archivos de fuentes a la carpeta de fuentes.

Inicializa las fuentes

  1. En la ventana del proyecto, abre ui.theme > Type.kt. Inicializa las fuentes descargadas debajo de las sentencias de importación y sobre val Typography. Primero, inicializa Abril Fatface. Para ello, configúrala igual que FontFamily y pasa Font con el archivo de fuente abril_fatface_regular.
​​import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import com.example.woof.R

val AbrilFatface = FontFamily(
   Font(R.font.abril_fatface_regular)
)
  1. Inicializa Montserrat, debajo de Abril Fatface. Para ello, establécela en FontFamily y pasa Font con el archivo de la fuente montserrat_regular. Para montserrat_bold, también incluye FontWeight.Bold. Aunque pases la versión en negrita del archivo de fuente, Compose no sabrá que está en negrita, por lo que deberás vincular el archivo a FontWeight.Bold de manera explícita.
import androidx.compose.ui.text.font.FontWeight

val AbrilFatface = FontFamily(
   Font(R.font.abril_fatface_regular)
)

val Montserrat = FontFamily(
   Font(R.font.montserrat_regular),
   Font(R.font.montserrat_bold, FontWeight.Bold)
)

A continuación, establece los diferentes tipos de títulos en las fuentes que acaba de agregar. El objeto Typography tiene parámetros para los 13 tipos de letra diferentes mencionados anteriormente. Puedes definir tantos como necesites. En esta app, configuraremos displayLarge, displayMedium y bodyLarge. En la próxima parte de esta app, usarás labelSmall, por lo que lo agregarás aquí.

A continuación, se muestra una tabla con la fuente, el grosor y el tamaño de cada encabezado que agregues.

8ea685b3871d5ffc.png

  1. En el caso del atributo displayLarge, configúralo igual que TextStyle y completa fontFamily, fontWeight y fontSize con la información de la tabla anterior. Esto significa que todo el texto establecido en displayLarge tendrá Abril Fatface como fuente, con un grosor de fuente normal y fontSize de 36.sp.

Repite este proceso con displayMedium, labelSmall y bodyLarge.

import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.sp

val Typography = Typography(
   displayLarge = TextStyle(
       fontFamily = AbrilFatface,
       fontWeight = FontWeight.Normal,
       fontSize = 36.sp
   ),
   displayMedium = TextStyle(
       fontFamily = Montserrat,
       fontWeight = FontWeight.Bold,
       fontSize = 20.sp
   ),
   labelSmall = TextStyle(
       fontFamily = Montserrat,
       fontWeight = FontWeight.Bold,
       fontSize = 14.sp
   ),
   bodyLarge = TextStyle(
       fontFamily = Montserrat,
       fontWeight = FontWeight.Normal,
       fontSize = 14.sp
   )
)

Si vas al archivo Theme.kt en WoofTheme() y observas el MaterialTheme(), el parámetro typography es igual al Typography val que acabas de actualizar.

MaterialTheme(
   colors = colors,
   typography = Typography,
   shapes = Shapes,
   content = content
)

Agrega tipografía al texto de la app

Ahora, agregarás los tipos de encabezado a cada instancia de texto de la app.

  1. Agrega displayMedium como estilo para dogName, ya que es información importante y breve. Agrega bodyLarge como estilo para dogAge, ya que funciona bien con tamaños de texto más pequeños.
@Composable
fun DogInformation(
   @StringRes dogName: Int,
   dogAge: Int,
   modifier: Modifier = Modifier
) {
   Column(modifier = modifier) {
       Text(
           text = stringResource(dogName),
           style = MaterialTheme.typography.displayMedium,
           modifier = Modifier.padding(top = dimensionResource(id = R.dimen.padding_small))
       )
       Text(
           text = stringResource(R.string.years_old, dogAge),
           style = MaterialTheme.typography.bodyLarge
       )
   }
}
  1. Ahora, en WoofPreview(), el nombre del perro muestra la fuente Montserrat en negrita en 20.sp, y la edad del perro muestra la fuente Montserrat normal en 14.sp.

Vista previa de Woof con la tipografía agregada

A continuación, se muestra una vista en paralelo de los elementos de lista antes y después de agregar una tipografía. Observa la diferencia de fuente entre el nombre y la edad del perro.

Sin tipografía

Con tipografía

7. Agrega una barra superior

Un objeto Scaffold es un diseño que proporciona ranuras para varios componentes y elementos de pantalla, como Image, Row o Column. Un Scaffold también proporciona una ranura para TopAppBar, que usarás en esta sección.

Un elemento TopAppBar se puede usar para muchos propósitos, pero en este caso, se usa para desarrollar la marca y darle personalidad a la app. Existen cuatro tipos diferentes de TopAppBar: central, pequeño, mediano y grande. En este codelab, implementarás una barra superior central de la app. Crearás un elemento componible que se vea como en la siguiente captura de pantalla y lo colocarás en la sección topBar de un Scaffold.

172417c7b64372f7.png

En el caso de esta app, nuestra barra superior consta de un Row con una imagen de un logotipo y texto del título de la app. El logotipo incluye una linda huella en degradé y el título de la app.

736f411f5067e0b5.png

Agrega imagen y texto a la barra superior

  1. En MainActivity.kt, crea un elemento componible llamado WoofTopAppBar() con un modifier opcional.
@Composable
fun WoofTopAppBar(modifier: Modifier = Modifier) {

}
  1. Scaffold admite el parámetro contentWindowInsets, que puede ayudar a especificar las inserciones para el contenido de Scaffold. WindowInsets son las partes de la pantalla en las que la app puede cruzarse con la IU del sistema. Se deben pasar a la ranura de contenido a través de los parámetros PaddingValues. Obtén más información en el siguiente vínculo.

El valor contentWindowInsets se pasa a LazyColumn como contentPadding.

@Composable
fun WoofApp() {
    Scaffold { it ->
        LazyColumn(contentPadding = it) {
            items(dogs) {
                DogItem(
                    dog = it,
                    modifier = Modifier.padding(dimensionResource(R.dimen.padding_small))
                )
            }
        }
    }
}
  1. Dentro de Scaffold, agrega un atributo topBar y establécelo en WoofTopAppBar().
Scaffold(
   topBar = {
       WoofTopAppBar()
   }
)

A continuación, se muestra cómo se verá el elemento componible WoofApp():

@Composable
fun WoofApp() {
    Scaffold(
        topBar = {
            WoofTopAppBar()
        }
    ) { it ->
        LazyColumn(contentPadding = it) {
            items(dogs) {
                DogItem(
                    dog = it,
                    modifier = Modifier.padding(dimensionResource(R.dimen.padding_small))
                )
            }
        }
    }
}

No cambió nada en WoofPreview() porque no hay nada en WoofTopAppBar(). Cambiemos eso.

Vista previa de Woof con tipografía

  1. Dentro de WoofTopAppBar() Composable, agrega CenterAlignedTopAppBar() y establece el parámetro modificador en el modificador que se pasó a WoofTopAppBar().
import androidx.compose.material3.CenterAlignedTopAppBar

@Composable
fun WoofTopAppBar(modifier: Modifier = Modifier) {
   CenterAlignedTopAppBar(
       modifier = modifier
   )
}
  1. Para el parámetro de título, pasa una Row que contendrá la Image y el Text de la CenterAlignedTopAppBar.
@Composable
fun WoofTopAppBar(modifier: Modifier = Modifier){
   CenterAlignedTopAppBar(
       title = {
           Row() {

           }
       },
       modifier = modifier
   )
}
  1. Agrega el logotipo Image a Row.
  • Configura el tamaño de la imagen en modifier como image_size en el archivo dimens.xml y el padding como padding_small del archivo dimens.xml.
  • Usa painter para establecer Image como ic_woof_logo de la carpeta del elemento de diseño.
  • Establece contentDescription como null. En esta situación, el logotipo de la app no agrega información semántica para los usuarios con discapacidades visuales, por lo que no es necesario agregar una descripción del contenido.
Row() {
   Image(
       modifier = Modifier
           .size(dimensionResource(id = R.dimen.image_size))
           .padding(dimensionResource(id = R.dimen.padding_small)),
       painter = painterResource(R.drawable.ic_woof_logo),
       contentDescription = null
   )
}
  1. A continuación, agrega un elemento componible Text dentro de Row después de Image.
  • Usa stringResource() para establecerlo en el valor de app_name. De esta forma, se establece el texto en el nombre de la app, que se almacena en strings.xml.
  • Establece el estilo del texto en displayLarge, ya que el nombre de la app es importante y breve.
Text(
   text = stringResource(R.string.app_name),
   style = MaterialTheme.typography.displayLarge
)

Vista previa de Woof con la barra superior de la app

Esto es lo que aparece en WoofPreview(); se ve un poco extraño porque el ícono y el texto no están alineados verticalmente.

  1. Para solucionar este problema, agrega un parámetro de valor verticalAlignment a Row y configúralo igual que Alignment.CenterVertically.
import androidx.compose.ui.Alignment

Row(
   verticalAlignment = Alignment.CenterVertically
)

Vista previa de Woof con la barra superior de la app centrada

Se ve mucho mejor.

Este es el elemento componible WoofTopAppBar() completo:

@Composable
fun WoofTopAppBar(modifier: Modifier = Modifier) {
   CenterAlignedTopAppBar(
       title = {
           Row(
               verticalAlignment = Alignment.CenterVertically
           ) {
               Image(
                   modifier = Modifier
                       .size(dimensionResource(id = R.dimen.image_size))
                       .padding(dimensionResource(id = R.dimen.padding_small)),
                   painter = painterResource(R.drawable.ic_woof_logo),

                   contentDescription = null
               )
               Text(
                   text = stringResource(R.string.app_name),
                   style = MaterialTheme.typography.displayLarge
               )
           }
       },
       modifier = modifier
   )
}

Ejecuta la app y admira cómo TopAppBar une estéticamente toda la app.

Sin la barra superior

Con la barra superior de la app

Ahora, observa la app final con tema oscuro.

2776e6a45cf3434a.png

¡Felicitaciones! Llegaste al final del codelab.

8. Obtén el código de la solución

Para descargar el código del codelab terminado, puedes usar estos comandos de git:

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-woof.git
$ cd basic-android-kotlin-compose-training-woof
$ git checkout material

También puedes descargar el repositorio como un archivo ZIP, descomprimirlo y abrirlo en Android Studio.

Descargar ZIP

Si deseas ver el código de la solución, puedes hacerlo en GitHub.

9. Conclusión

Acabas de crear tu primera app de Material. Creaste una paleta de colores personalizada para los temas claro y oscuro, generaste formas para diferentes componentes, descargaste fuentes y las agregaste a la app, y creaste una atractiva barra superior para unir todo. Toma las habilidades que aprendiste en este codelab y cambia los colores, las formas y la tipografía para personalizar tus apps de principio a fin.

Resumen

  • Los Temas de Material te permiten adaptar Material Design a tu app, con lineamientos para personalizar los colores, la tipografía y las formas.
  • El archivo Theme.kt es donde se define el tema, a través de un elemento componible llamado [your app name]+Theme()WoofTheme() en el caso de esta app. Dentro de esta función, el objeto MaterialTheme establece los elementos color, typography, shapes y content de la app.
  • Color.kt es donde incluyes los colores que usas en la app. Luego, en Theme.kt, asignas los colores en LightColorPalette y DarkColorPalette a ranuras específicas. No es necesario asignar todas las ranuras disponibles.
  • La app puede aceptar Forzar oscuro, lo que significa que el sistema implementará el Tema oscuro por ti. Sin embargo, si implementas el tema oscuro, los usuarios tendrán una mejor experiencia, lo que a su vez te permitirá tener el control total sobre el tema de la app.
  • Shape.kt es donde defines las formas de tu app. Hay tres tamaños de forma (pequeño, mediano y grande) y puedes especificar cómo se redondean las esquinas.
  • Las formas dirigen la atención, identifican los componentes, comunican el estado y expresan la marca.
  • Type.kt es donde inicializas las fuentes y asignas fontFamily, fontWeight y fontSize para la escala de tipos de Material Design.
  • La escala de tipos de Material Design incluye una variedad de estilos contrastantes que satisfacen las necesidades de tu app y su contenido. La escala de tipos es una combinación de 15 estilos que admite el sistema de tipos.

10. Más información