Noticia Gestión de temas oscuros y claros automáticamente con Compose Theming

Gestión de temas oscuros y claros automáticamente con Compose Theming


Si te has aventurado en el mundo del desarrollo Android, sabrás que gestionar la apariencia de una aplicación solía ser un auténtico quebradero de cabeza. Aquellos días de pelearse con archivos XML infinitos y estilos que solo entendía el programador que los creó quedaron atrás gracias a que Jetpack Compose ha cambiado las reglas del juego, trayendo consigo un enfoque declarativo donde el diseño fluye de manera mucho más natural.

En este sentido, dominar la alternancia entre el modo claro y el modo oscuro no es solo una cuestión de estética, sino un pilar de la experiencia de usuario y la accesibilidad. No se trata solo de poner la pantalla en negro, sino de crear un ecosistema visual que se adapte al entorno del usuario, cuide su vista y, de paso, le ayude a que la batería del móvil dure un poco más.

Los fundamentos del modo oscuro en Android​


Desde Android 10 en adelante, el sistema operativo ofrece un soporte nativo para el tema oscuro que aporta beneficios tangibles. No solo reduce el gasto energético en pantallas OLED, sino que es fundamental para personas con sensibilidad a la luz brillante. El sistema permite habilitarlo desde los ajustes de pantalla, mediante tarjetas de configuración rápida o, en ciertos dispositivos Pixel, vinculándolo al modo de ahorro de energía.

Para que una aplicación sea compatible, lo ideal es que herede de temas DayNight de AppCompat o Material Components. Esto permite que la interfaz reaccione automáticamente a los cambios del sistema. Un punto clave aquí es evitar los colores «hardcodeados» o fijos; en su lugar, debemos apoyarnos en atributos de tema como textColorPrimary, que cambia su tono según el modo activo, asegurando que el texto siempre sea legible.

Diseñando con Material Design 3 en Compose​


Cuando abrimos un proyecto nuevo en Compose, nos encontramos con una estructura en la carpeta de UI que divide el tema en tres pilares: colores, tipografía y formas. El MaterialTheme es la pieza central que envuelve nuestra aplicación y distribuye estas configuraciones a todos los componentes hijos.

El ecosistema de colores y roles​


Material 3 no se limita a dos paletas fijas, sino que utiliza un sistema de roles. El color Primary es el protagonista para acciones principales, mientras que el Secondary se reserva para elementos menos llamativos, como chips de filtros. El color Tertiary se emplea para generar contrastes específicos.

Para evitar errores de contraste, se utilizan colores «On», como onPrimary o onSurface. Estos colores están diseñados específicamente para colocarse sobre su respectivo fondo, garantizando que cualquier usuario, independientemente de su capacidad visual, pueda leer la interfaz sin esfuerzo. Además, herramientas como el Material Theme Builder permiten exportar directamente el código de Kotlin basado en un color semilla de marca, facilitando la creación de esquemas claros y oscuros coherentes.

Tipografía y Formas: Más allá del color​


La tipografía en M3 se organiza en categorías como Headings, Subtitles, Body, Button, Caption y Overline. Al personalizar la clase Typography en Compose, podemos definir pesos de fuente y tamaños específicos (en sp) para cada rol, logrando que la jerarquía visual sea clara. Por ejemplo, usar un estilo SemiBold para los títulos ayuda a que el usuario escanee la información rápidamente.

En cuanto a las formas, el sistema define escalas desde extra-pequeña hasta extra-grande. Podemos usar RoundedCornerShape para bordes suaves o CutCornerShape si buscamos una estética más angular. Aplicar estas formas a través de MaterialTheme.shapes asegura que todos los botones y tarjetas de la app mantengan una identidad visual uniforme.

Arquitectura avanzada para la gestión de temas​


Para llevar un sistema de temizado a un nivel profesional, no basta con un simple booleano. Lo ideal es implementar una arquitectura de estado de IU compartida combinada con Kotlin Flows. Utilizando un enum como ThemeMode (System, Light, Dark), podemos gestionar la preferencia del usuario de forma tipada y segura.

La persistencia de estos ajustes se logra mediante Jetpack DataStore Preferences, que sustituye al antiguo SharedPreferences ofreciendo un manejo asíncrono y reactivo. El repositorio expone un flujo de datos que el ViewModel convierte en un StateFlow mediante stateIn, permitiendo que la UI se actualice instantáneamente cuando el usuario cambia el tema en los ajustes, sin necesidad de reiniciar la actividad manualmente.

Colores dinámicos y Material You​


A partir de Android 12, tenemos la posibilidad de implementar colores dinámicos. Esta tecnología analiza el fondo de pantalla del usuario y genera una paleta de colores armonizada que se aplica a toda la app. En Compose, esto se gestiona detectando la versión del SDK y utilizando los proveedores de color dinámico, lo que hace que la aplicación se sienta como una extensión natural del sistema operativo.

Consideraciones técnicas y optimizaciones​


Un aspecto crítico es el manejo de los cambios de configuración. Cuando el usuario cambia el tema, Android recrea la actividad. Para evitar parpadeos o interrupciones, como ocurre durante la reproducción de un vídeo, podemos declarar en el manifiesto que la actividad controlará el cambio de uiMode, gestionando la transición mediante el método onConfigurationChanged().

En cuanto al rendimiento, es vital usar distinctUntilChanged en los flujos de datos para evitar que la aplicación realice recomposiciones innecesarias cada vez que se emite un valor idéntico desde el repositorio. Asimismo, el uso de collectAsStateWithLifecycle vincula la recolección del estado del tema al ciclo de vida de la pantalla, evitando fugas de memoria y consumo de recursos en segundo plano.

Existen también soluciones externas más agresivas, como apps que utilizan el sensor de luz ambiental para cambiar el tema en tiempo real basándose en los luxes detectados, aunque esto requiere permisos especiales de sistema (WRITE_SECURE_SETTINGS). En la mayoría de los casos, una implementación robusta de Material 3 junto a una persistencia reactiva es más que suficiente para ofrecer una experiencia de usuario premium.

Implementar una gestión de temas profesional implica unir la potencia de Material Design 3 con una arquitectura reactiva basada en Flow y DataStore, asegurando que la transición entre modo claro, oscuro y dinámico sea fluida, accesible y coherente en todas las versiones de Android.

Continúar leyendo...