Sistemas de diseño, Material 3 y accesibilidad en Android
Tokens, temas, componentes, Compose y accesibilidad
Introducción
Una aplicación puede cumplir todos sus requisitos funcionales y, aun así, presentar una interfaz difícil de utilizar: botones inconsistentes, textos ilegibles, campos que se ocultan al abrir el teclado o pantallas que se desorganizan al aumentar el tamaño de letra. La calidad de una interfaz requiere decisiones coherentes sobre jerarquía visual, interacción y accesibilidad.
Un sistema de diseño organiza esas decisiones para reutilizarlas. En Android, Material Design 3 y Jetpack Compose proporcionan mecanismos para definir temas de color, tipografía y formas; crear componentes personalizados; y probar sus variaciones. Esta guía explica cómo se relacionan estos mecanismos y cómo aplicarlos a diferentes proyectos.
Al finalizar el estudio deberías poder distinguir tokens, componentes y pantallas; configurar temas claros y oscuros; construir componentes reutilizables; gestionar su estado local; identificar problemas de distribución con textos grandes; y justificar decisiones básicas de accesibilidad.
Idea central: un token expresa una decisión compartida; un componente materializa un patrón de interacción; una pantalla organiza componentes para cumplir una tarea.
Lo imprescindible para el examen
Distinguir tokens, componentes y pantallas; interpretar temas Material 3 y tipografía; explicar Modifier y estado local; reconocer problemas de contraste y texto ampliado.
Se evalúa la capacidad de explicar, interpretar fragmentos y justificar decisiones. Las versiones puntuales de librerías, URLs, nombres de proyecto y comandos accesorios quedan como contexto de consulta.
Criterios transversales para interpretar esta guía
Principio transferible. Conviene distinguir la responsabilidad de una tecnología de la forma concreta en que se utilizó en la práctica. Un ejemplo de ViewModel, Retrofit, Hilt o WebSocket ilustra una solución posible; su configuración exacta no es una regla universal.
Decisión del proyecto. Los nombres, rutas, versiones, intervalos, tamaños y elecciones de librerías del caso original sirven para interpretar el código. Salvo indicación expresa, no se exige memorizarlos para el examen.
Alcance del código. Los fragmentos son recursos didácticos; pueden omitir imports, dependencias, permisos, detalles de manejo de errores o infraestructura. Comprender un fragmento no equivale a comprobar que compila como proyecto independiente.
Referencias cruzadas. La síntesis transversal de la colección reúne las distinciones entre estado y evento, persistencia, reintentos, idempotencia y límites de las pruebas.
1. ¿Qué es un sistema de diseño?
Un sistema de diseño es un conjunto de decisiones, reglas y componentes reutilizables que permite construir interfaces consistentes. Abarca aspectos visuales y también comportamientos, estados, accesibilidad y convenciones de interacción.
No debe confundirse con un conjunto de capturas o pantallas terminadas. Las capturas muestran resultados concretos; el sistema permite construir pantallas nuevas siguiendo criterios comunes.
| Nivel | Responsabilidad | Ejemplo |
|---|---|---|
| Token | Definir una decisión compartida con significado | primary, bodyMedium, spacing.large |
| Componente | Encapsular apariencia e interacción reutilizables | Botón con progreso, campo de contraseña, tarjeta |
| Pantalla | Distribuir componentes de acuerdo con una tarea | Formulario de acceso, catálogo, detalle |
Caso de aplicación. Una app de biblioteca incluye un catálogo, una ficha de libro y un formulario de préstamo. El radio de las tarjetas pertenece al tema; la posición de título y autor pertenece al componente de tarjeta; decidir agrupar los libros por categoría pertenece a la pantalla.
Criterio de decisión: preguntarse dónde se reutiliza algo y qué elementos deberían cambiar juntos. No todas las propiedades repetidas necesitan convertirse en tokens globales.
2. Tokens semánticos frente a valores literales
Un valor literal describe una implementación (Color(0xFF006B55), 16.dp). Un token semántico expresa un propósito (primary, spacing.large).
// Acoplamiento a un valor concreto
Text("Continuar", color = Color(0xFF006B55))
// Dependencia de una decisión del tema
Text("Continuar", color = MaterialTheme.colorScheme.primary)
La segunda forma permite cambiar el tema sin modificar todos los lugares donde se emplea ese rol.
Matiz importante. No todo número literal es un error. Un borde de 1.dp, una línea divisoria o una dimensión propia de un componente pueden ser decisiones locales justificadas. Conviene centralizar las decisiones compartidas, sin crear tokens para cualquier constante.
2.1 Cohesión y escalabilidad
Un buen token mantiene su significado aunque cambie su valor. Si el color de marca cambia de verde a azul, primary continúa representando una acción principal. Un nombre como VerdeMarca queda ligado a una apariencia anterior.
La centralización también tiene límites: un tema global no debe contener datos de negocio, el estado de la sesión ni dependencias de red.
3. Colores y roles de Material 3
Material 3 utiliza un ColorScheme con roles como primary, onPrimary, primaryContainer, onPrimaryContainer, secondary, tertiary, error y diversas superficies. Los roles describen usos, no colores fijos.
| Roles | Uso habitual |
|---|---|
primary / onPrimary |
Acción principal y contenido colocado encima |
primaryContainer / onPrimaryContainer |
Contenedor destacado y su contenido |
secondary / onSecondary |
Acentos secundarios |
error / onError |
Elementos que comunican errores |
surface / onSurface |
Superficies y contenido general |
surfaceContainerLow, surfaceContainerHigh |
Variantes de superficies para jerarquía |
val ColoresClaros = lightColorScheme(
primary = Color(0xFF006B55),
onPrimary = Color.White,
primaryContainer = Color(0xFF9EF2D5),
onPrimaryContainer = Color(0xFF00513F)
)
val ColoresOscuros = darkColorScheme(
primary = Color(0xFF82D5BA),
onPrimary = Color(0xFF00382B),
primaryContainer = Color(0xFF005140),
onPrimaryContainer = Color(0xFF9EF2D5)
)
En una pantalla:
Surface(color = MaterialTheme.colorScheme.primaryContainer) {
Text(
text = "Novedad",
color = MaterialTheme.colorScheme.onPrimaryContainer
)
}
Los pares color / onColor están pensados para funcionar juntos. Utilizar roles emparejados no sustituye la comprobación real del contraste, especialmente si el tema se ha personalizado manualmente o hay transparencias, imágenes y estados especiales.
3.1 Color semilla y paletas tonales
Una semilla permite generar paletas tonales, de las que se asignan valores a los roles. Herramientas como Material Theme Builder facilitan producir esquemas coherentes para modo claro y oscuro. Una modificación aislada de primary no actualiza automáticamente primaryContainer y los demás roles; puede romper la armonía visual.
3.2 Color dinámico
Material You puede generar colores a partir de preferencias del dispositivo, incluidos colores del fondo de pantalla en versiones compatibles de Android. Usarlo es una decisión de producto: favorece la personalización del dispositivo; desactivarlo favorece la identidad visual fija. No existe una obligación general de desactivarlo en aplicaciones con marca.
4. Tipografía: jerarquía y escalas
La tipografía comunica qué se debe leer primero. Material 3 ofrece 15 estilos organizados en cinco familias, cada una con tamaños Large, Medium y Small.
| Familia | Función frecuente |
|---|---|
display |
Textos de gran protagonismo |
headline |
Encabezados de secciones importantes |
title |
Títulos de tarjetas, diálogos y bloques |
body |
Contenido de lectura |
label |
Etiquetas y controles |
private val tipografiaBase = Typography()
val TipografiaApp = Typography(
titleLarge = tipografiaBase.titleLarge.copy(
fontWeight = FontWeight.SemiBold
),
bodyMedium = tipografiaBase.bodyMedium.copy(
fontSize = 15.sp,
lineHeight = 22.sp
)
)
En las pantallas es preferible pedir estilos:
Text(
text = "Libros disponibles",
style = MaterialTheme.typography.titleLarge
)
4.1 sp y dp
spes la unidad habitual para tamaños tipográficos; incorpora la preferencia de escala de texto del usuario.dpexpresa dimensiones de interfaz independientes de densidad, como separaciones y tamaños de contenedores.
El uso de sp no garantiza automáticamente accesibilidad: al ampliar el texto, la distribución debe permitir saltos de línea, desplazamiento y crecimiento de componentes.
4.2 Fuentes personalizadas
Las fuentes empaquetadas pueden declararse como recursos en res/font/, con nombres válidos para Android. Las fuentes estáticas permiten asociar archivos a pesos concretos:
val FuenteCuerpo = FontFamily(
Font(R.font.cuerpo_regular, FontWeight.Normal),
Font(R.font.cuerpo_semibold, FontWeight.SemiBold)
)
El ejemplo presupone que esos recursos existen. En la práctica original se emplean dos familias diferentes para títulos y lectura continua. Es una decisión de diseño, no un requisito de Material 3. Para usar archivos descargados se debe comprobar el formato, el rango de versiones de Android y la licencia de redistribución.
5. Formas y espaciado
Material 3 proporciona Shapes, que permite centralizar distintos radios y estilos de forma.
val FormasApp = Shapes(
extraSmall = RoundedCornerShape(6.dp),
small = RoundedCornerShape(8.dp),
medium = RoundedCornerShape(12.dp),
large = RoundedCornerShape(16.dp),
extraLarge = RoundedCornerShape(24.dp)
)
En un componente:
Card(shape = MaterialTheme.shapes.extraLarge) {
Text("Contenido", modifier = Modifier.padding(16.dp))
}
Para el espaciado, que no está incluido como escala personalizada en MaterialTheme, puede definirse una estructura propia:
@Immutable
data class Espaciado(
val pequeno: Dp = 8.dp,
val mediano: Dp = 12.dp,
val grande: Dp = 16.dp,
val extraGrande: Dp = 24.dp
)
Una escala reduce variaciones accidentales y facilita mantener un ritmo visual. No implica que cualquier dimensión deba pertenecer a la escala: las dimensiones funcionales de ciertos controles pueden tener valores propios.
6. CompositionLocal y personalización del tema
En Compose, un CompositionLocal permite proporcionar datos a un subárbol sin pasarlos como parámetro por cada nivel. MaterialTheme usa mecanismos de este tipo para exponer el esquema de color y la tipografía.
val LocalEspaciado = staticCompositionLocalOf { Espaciado() }
@Composable
fun AppTheme(
oscuro: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
CompositionLocalProvider(LocalEspaciado provides Espaciado()) {
MaterialTheme(
colorScheme = if (oscuro) ColoresOscuros else ColoresClaros,
typography = TipografiaApp,
shapes = FormasApp,
content = content
)
}
}
Lectura desde un componente descendiente:
@Composable
fun TarjetaInformativa(titulo: String) {
val espacio = LocalEspaciado.current
Card {
Text(titulo, modifier = Modifier.padding(espacio.grande))
}
}
6.1 compositionLocalOf y staticCompositionLocalOf
La versión staticCompositionLocalOf resulta apropiada para valores relativamente estables, como una configuración de espaciado. Si cambia el valor proporcionado, puede recomponerse un ámbito más amplio. compositionLocalOf registra lecturas y permite recomposiciones más específicas. La elección depende de cómo cambia la información y de quién la consume.
No conviene usar CompositionLocal para ocultar cualquier dependencia. Para datos de negocio, callbacks o parámetros esenciales de un componente, las propiedades explícitas suelen mejorar la comprensión, reutilización y facilidad de prueba.
7. Diseñar componentes reutilizables
Un componente reutilizable ofrece una interfaz clara de parámetros y concentra decisiones que de otro modo se repetirían en numerosas pantallas.
Principios útiles:
- Recibir los datos y callbacks necesarios para funcionar.
- Exponer
modifier: Modifier = Modifiercuando corresponda y aplicarlo al elemento raíz. - Tomar del tema los colores, estilos y formas compartidos.
- Ofrecer valores por omisión cuando exista una alternativa razonable.
- Mantener en el componente el estado estrictamente local a su presentación.
- Permitir visualizar sus diferentes estados de forma aislada.
7.1 Un botón con estado de carga
@Composable
fun BotonAccion(
texto: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
habilitado: Boolean = true,
cargando: Boolean = false
) {
Button(
onClick = onClick,
enabled = habilitado && !cargando,
modifier = modifier.fillMaxWidth().heightIn(min = 56.dp)
) {
if (cargando) {
CircularProgressIndicator(
modifier = Modifier.size(18.dp),
strokeWidth = 2.dp,
color = LocalContentColor.current
)
Spacer(Modifier.width(8.dp))
}
Text(texto)
}
}
El parámetro cargando viene de la pantalla o su estado de UI. El componente decide cómo representarlo. Deshabilitar el botón reduce acciones repetidas, aunque la capa que ejecuta la operación también debe impedir envíos duplicados si es necesario.
7.2 Un campo de contraseña
El texto introducido suele pertenecer al estado del formulario; la decisión de mostrarlo u ocultarlo puede ser local al componente.
@Composable
fun CampoContrasena(
valor: String,
onValorChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
var visible by rememberSaveable { mutableStateOf(false) }
OutlinedTextField(
value = valor,
onValueChange = onValorChange,
modifier = modifier.fillMaxWidth(),
label = { Text("Contraseña") },
singleLine = true,
visualTransformation =
if (visible) VisualTransformation.None
else PasswordVisualTransformation(),
trailingIcon = {
TextButton(onClick = { visible = !visible }) {
Text(if (visible) "Ocultar" else "Mostrar")
}
}
)
}
rememberSaveable guarda el booleano de visibilidad, no la contraseña. No debe interpretarse como recomendación de persistir credenciales en estado restaurable.
7.3 Componente visual y modelo de dominio
Un componente puede recibir valores preparados para visualización (titulo, subtitulo, fechaFormateada) y así reutilizarse en distintas pantallas. También puede existir una función adaptadora que reciba un modelo de dominio y traduzca sus propiedades. Esta adaptación debe evitar mezclar la lógica de negocio con la representación.
8. Pantallas, composición y organización visual
Una pantalla organiza componentes y gestiona su disposición; no necesita definir de nuevo los mismos colores, radios o estilos. Algunas decisiones típicas de pantalla son agrupar registros por fecha, elegir acciones principales, colocar botones respecto al teclado o habilitar desplazamiento.
8.1 Componentes de listas
Una LazyColumn permite presentar listas que pueden desplazarse. Cuando se necesita incluir una cabecera que participe del scroll, puede usarse item { ... } antes de los elementos de la lista.
LazyColumn {
item { Text("Catálogo", style = MaterialTheme.typography.titleLarge) }
items(productos, key = { it.id }) { producto ->
TarjetaProducto(producto = producto)
}
}
El ejemplo presupone un modelo Producto con id y un componente TarjetaProducto definido en la app.
8.2 Formularios y teclado
Un formulario debe conservar acciones accesibles cuando aparece el teclado y cuando el texto aumenta. Según la estructura pueden combinarse verticalScroll, imePadding y el manejo de insets. Si hay un botón fijo en la parte inferior, conviene verificarlo con el teclado visible y en ventanas pequeñas.
Column(
modifier = Modifier
.fillMaxSize()
.imePadding()
.verticalScroll(rememberScrollState())
.padding(16.dp)
) {
// Campos del formulario y acción principal.
}
No es una receta universal: el tratamiento del teclado depende de si la pantalla usa Scaffold, barras de navegación, bottomBar u otros contenedores.
8.3 Estado vacío y límites de tamaño
En un LazyColumn, el tamaño disponible para ciertos hijos puede estar sin acotar en la dirección del desplazamiento. Por ello, fillMaxSize() dentro de un elemento de lista no siempre tiene el comportamiento esperado. Alternativas como fillParentMaxHeight o un contenedor exterior con altura acotada deben elegirse según el diseño.
9. Accesibilidad: criterios comprobables
La accesibilidad requiere probar el sistema en condiciones distintas a las utilizadas durante el desarrollo. No basta con adoptar una paleta visualmente atractiva.
| Criterio | Qué verificar |
|---|---|
| Contraste | Legibilidad entre texto, íconos y fondos |
| Tamaño de texto | Que la interfaz admita ampliaciones como 200 % |
| Objetivos táctiles | Áreas fáciles de alcanzar y activar |
| Semántica | Descripciones adecuadas para lectores de pantalla |
| Orden de lectura | Secuencia comprensible de navegación y foco |
| Estados | No depender exclusivamente del color para informar |
| Movimiento | Evitar animaciones innecesarias o molestas |
Como referencia de WCAG AA, para texto convencional se utiliza normalmente 4.5:1 y para texto grande 3:1. El contraste de los componentes no textuales importantes suele evaluarse con un umbral de 3:1. Estos criterios dependen del elemento y del contexto; no debe aplicarse mecánicamente una sola cifra a todo.
En Android es una buena referencia ofrecer un objetivo táctil de al menos 48 × 48 dp. El ícono visible puede ser menor si su zona interactiva respeta ese tamaño.
9.1 Por qué se rompen las filas con texto grande
En una Row, los elementos sin weight suelen medirse antes que los que distribuyen el espacio sobrante mediante weight. Un texto extenso sin restricciones puede dejar muy poco ancho para otro texto que utiliza weight, provocando saltos de línea excesivos.
Opciones de solución:
- Cambiar a una
Columny apilar título y descripción. - Definir qué dato puede truncarse con
maxLinesyTextOverflow.Ellipsis. - Permitir que textos importantes ocupen varias líneas.
- Revisar la prioridad visual de etiquetas, tiempos y metadatos.
La solución debe preservar la información importante y la posibilidad de interacción, no limitarse a reducir el tamaño de letra.
9.2 Descripciones y elementos decorativos
Los íconos con significado propio necesitan una descripción accesible. Los elementos decorativos pueden utilizar contentDescription = null cuando su significado ya está comunicado por texto cercano.
Icon(
imageVector = Icons.Default.Warning,
contentDescription = "Advertencia"
)
En componentes compuestos puede ser mejor establecer semántica en el contenedor para evitar anuncios repetitivos.
10. @Preview y comprobación de variantes
Las vistas previas permiten estudiar componentes independientemente de una sesión, una API o un emulador. Son útiles como catálogo visual de los estados de un sistema de diseño.
@Preview(name = "Claro", showBackground = true)
@Composable
private fun AccionPreviewClaro() {
AppTheme(oscuro = false) {
BotonAccion(texto = "Guardar", onClick = {})
}
}
@Preview(name = "Oscuro", showBackground = true)
@Composable
private fun AccionPreviewOscuro() {
AppTheme(oscuro = true) {
BotonAccion(texto = "Guardando", onClick = {}, cargando = true)
}
}
Los @Preview ayudan a detectar problemas tempranos; no sustituyen pruebas automatizadas ni pruebas de accesibilidad en dispositivos reales. Es recomendable combinar vistas previas, comprobaciones de accesibilidad disponibles en Android Studio y recorridos manuales con texto ampliado, modo oscuro y teclado visible.
Conexión transversal: alcance y decisiones
¿Qué demuestra una revisión de accesibilidad?
Una @Preview permite inspeccionar variantes visuales; una prueba de Compose verifica nodos y propiedades semánticas concretas; ninguna de las dos acredita por sí sola una experiencia completa con TalkBack. La verificación debe incluir tamaño del texto, contraste real, orden y etiquetas de navegación, objetivos táctiles y pruebas manuales de interacción con tecnologías de apoyo. Véase la guía 11 sobre el alcance de las pruebas.
Principio: la interfaz debe adaptarse a las preferencias de accesibilidad. Decisión de la práctica: utilizar una paleta de marca sin color dinámico; otras aplicaciones pueden adoptar Material You sin incumplir ese principio.
11. Problemas frecuentes y diagnóstico
| Síntoma | Causa posible | Qué revisar |
|---|---|---|
| Los componentes tienen colores diferentes | Valores literales en lugar de roles | Centralizar decisiones compartidas en el tema |
| El texto tiene poco contraste | Roles mezclados o paleta mal ajustada | Pares primary/onPrimary y medición de contraste |
| En modo oscuro quedan superficies claras | Colores fijos o tema incompleto | darkColorScheme y valores locales |
| La nueva tipografía no aparece | Recurso mal declarado o formato incompatible | res/font, nombres, formato y fuentes |
| Los márgenes cambian entre pantallas | Uso inconsistente de valores sueltos | Escala de espaciado |
| No puede modificarse el ancho del componente | Se ignora el modifier recibido |
Aplicarlo al elemento raíz, respetando su contrato |
| El teclado tapa el botón principal | No se gestionan teclado e insets | imePadding, scroll y distribución |
| La letra grande rompe una fila | Hijos con ancho limitado o mal priorizado | weight, truncamiento y distribución vertical |
| Una vista previa se ve sin el tema | El composable no está envuelto en el tema | AppTheme dentro del @Preview |
| Un icono no se entiende con lector de pantalla | Semántica insuficiente | contentDescription y semántica combinada |
| Un estado vacío no queda centrado | Restricciones de tamaño dentro de un elemento de lista | Altura disponible y contenedor de la pantalla |
12. Vocabulario esencial
| Concepto | Definición |
|---|---|
| Sistema de diseño | Conjunto de decisiones y componentes reutilizables para interfaces coherentes |
| Token | Valor con significado y uso compartido |
| Token semántico | Token nombrado por su función, no por su apariencia concreta |
ColorScheme |
Conjunto de roles cromáticos de Material 3 |
| Paleta tonal | Variaciones de luminosidad empleadas para asignar colores a roles |
| Tipografía | Escala de estilos de texto de la interfaz |
Shapes |
Configuración compartida de formas y radios |
CompositionLocal |
Mecanismo para proporcionar valores a descendientes del árbol Compose |
| Componente reutilizable | Pieza de UI con contrato de parámetros y comportamiento definido |
Modifier |
Mecanismo de Compose para modificar diseño, apariencia o interacción |
@Preview |
Vista previa de un composable en herramientas de desarrollo |
| Accesibilidad | Propiedades que facilitan la interacción con distintas capacidades y configuraciones |
| Objetivo táctil | Área disponible para activar un control |
13. Preguntas de autoevaluación
Comprensión conceptual
- ¿Cuál es la diferencia entre un token, un componente y una pantalla? Da un ejemplo distinto de los vistos en la guía.
- ¿Por qué conviene usar el rol
primaryen lugar de un color hexadecimal directamente en varias pantallas? - ¿Por qué cambiar únicamente
primarypuede generar inconsistencias visuales? - ¿Qué diferencia existe entre personalizar un tema oscuro y activar colores dinámicos del dispositivo?
- ¿Cuáles son las cinco familias tipográficas de Material 3 y para qué se utilizan?
- ¿Por qué un texto definido en
sppuede seguir generando problemas de accesibilidad? - ¿En qué circunstancias es adecuado utilizar
CompositionLocaly cuándo convienen parámetros explícitos?
Interpretación de código
- ¿Qué se obtiene al leer
MaterialTheme.colorScheme.onPrimaryContainer? ¿Sobre qué fondo debería utilizarse normalmente? - Analiza un componente que recibe
modifier, pero lo ignora. ¿Qué limitaciones produce para la pantalla que lo utiliza? - En
BotonAccion, ¿qué sucede cuandocargando = true? ¿Es suficiente para garantizar que nunca habrá operaciones duplicadas? - ¿Por qué
CampoContrasenaconservavisibleen unrememberSaveablemientras recibevalorcomo parámetro? - ¿Qué sucede si un
@Previewutiliza un componente fuera deAppTheme? - Una fila contiene dos textos y solo el primero usa
Modifier.weight(1f). ¿Qué problemas podrían aparecer al ampliar la fuente?
Aplicación y diagnóstico
- Una app tiene siete pantallas y cada una define sus propios márgenes. ¿Qué cambio arquitectónico propondrías?
- Un botón muestra únicamente un ícono pequeño, difícil de tocar. ¿Cómo corregirías la interacción sin agrandar necesariamente el dibujo?
- Con texto al 200 %, el formulario deja fuera de la pantalla el botón «Enviar». ¿Qué revisarías?
- Un desarrollador utiliza
onSurfacesobreprimaryContainer. ¿Qué problema potencial existe y cómo lo comprobarías? - Quieres reutilizar la misma tarjeta en una lista y en una pantalla de confirmación sin disponer todavía del objeto de dominio. ¿Cómo diseñarías la interfaz del componente?
- El equipo quiere imponer una paleta fija para la marca, pero los usuarios solicitan personalización mediante Material You. ¿Qué alternativas tienen?
- ¿Qué pruebas mínimas aplicarías a un nuevo componente antes de integrarlo en tres pantallas distintas?
14. Respuestas posibles
- El token define una decisión compartida (por ejemplo, el tamaño de un texto); el componente encapsula la representación de un elemento (tarjeta de préstamo); la pantalla organiza componentes para una función (lista de préstamos).
- El rol conserva su sentido cuando cambia la identidad visual y permite mantener el estilo desde el tema, sin editar cada pantalla.
- Los roles forman conjuntos relacionados: pueden quedar contenedores y colores de contenido con tonos incompatibles. Se recomienda revisar la paleta completa.
- El tema oscuro asigna colores para una condición de luminosidad; el color dinámico obtiene valores desde las preferencias del dispositivo. Pueden coexistir según la política del producto.
display,headline,title,bodyylabel, cada una en tamaños grande, mediano y pequeño, para establecer jerarquía y funciones de lectura.- Porque el texto se amplía, pero el ancho y alto de los contenedores no necesariamente se adaptan; hay que probar distribución, scroll y saltos de línea.
- Es apropiado para dependencias de tema ampliamente compartidas. Para datos particulares de negocio, callbacks o decisiones esenciales, los parámetros explícitos suelen hacer más claro el contrato.
- Es el color previsto para contenido situado sobre
primaryContainer; no es un color universal para cualquier fondo. - La pantalla pierde control sobre la distribución, como el ancho, separaciones y posicionamiento externo; reduce la reutilización del componente.
- Se deshabilita el botón y aparece el indicador; la capa que procesa el evento debe mantener controles de concurrencia o idempotencia cuando corresponda.
visiblees un detalle local de presentación;valorpertenece al formulario y es controlado por su dueño. No se debe guardar la contraseña como consecuencia de restaurar la visibilidad.- Verá el tema del contexto disponible (o valores predeterminados) y puede no representar el producto real; conviene envolver todas las vistas previas con el tema de la app.
- El texto sin
weightpuede ocupar gran parte del ancho disponible y dejar al otro muy estrecho. Se debe revisar qué puede truncarse y si es preferible apilar el contenido. - Crear una escala de espaciado semántica compartida y reemplazar los valores inconsistentes, sin convertir indiscriminadamente todas las medidas en tokens.
- Ampliar la región interactiva para cumplir una referencia de al menos 48 × 48 dp y mantener el ícono visible con tamaño apropiado.
- Revisar scroll, teclado, insets, altura del contenido, distribución del botón y comportamiento en pantalla pequeña.
- La combinación puede tener contraste insuficiente porque son roles sin pareja directa; hay que medir el contraste en ambos temas y corregir la asignación.
- Diseñar una versión base que reciba los textos y valores listos para mostrar, y una adaptación opcional desde el modelo de dominio. Así la misma tarjeta sirve para datos reales y vistas previas.
- Pueden permitir preferencia entre tema de marca y colores dinámicos, mantener la paleta corporativa en elementos clave o desactivar el modo dinámico mediante una decisión de producto justificada y probada.
- Revisar estados normal, deshabilitado, cargando y error si aplican; tema claro y oscuro; fuente aumentada; área táctil; semántica accesible; y uso con restricciones de tamaño distintas.
15. Síntesis de estudio
Antes del examen, comprueba que puedes explicar qué problema resuelve un sistema de diseño, identificar la responsabilidad de cada nivel (tokens, componentes y pantallas), interpretar código de MaterialTheme, definir componentes parametrizables y diagnosticar problemas de accesibilidad. Los ejemplos no requieren recordar nombres ni pantallas de una práctica particular.
Referencias de consulta
- Android Developers. Material Design 3 in Compose. https://developer.android.com/develop/ui/compose/designsystems/material3
- Android Developers. Accessibility in Jetpack Compose. https://developer.android.com/develop/ui/compose/accessibility
- Material Design. Material Theme Builder. https://material-foundation.github.io/material-theme-builder/
Material adaptado de la Práctica 7 «Avisos con diseño» y de su guía de repaso. Se han generalizado los ejemplos y precisado algunas recomendaciones para distinguir decisiones didácticas de principios aplicables a otros proyectos.