Cómo implementar el modo oscuro en Kotlin para Android


Ahora que ya se ha hecho realidad el esperado modo oscuro en Android, gracias a la versión Android Q, en versiones anteriores de Android ya se vio un intento pero por ejemplo en Android P, solo se activaba cuando el dispositivo entraba en ahorro de batería.

Añadir soporte a tema oscuro

Para integrar modo oscuro en la aplicación lo puedes realizar con AppCompat o bien MaterialComponents ambas maneras se hace de la misma forma, heredar el tema principal de DayNight

<style name="AppTheme" parent="Theme.AppCompat.DayNight">

Usar tema oscuro de MaterialComponents

<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">

Permiten diferentes variaciones, en apunte me centrare con el nuevo estilo MaterialComponents

  • Theme.MaterialComponents.DayNight
  • Theme.MaterialComponents.DayNight.NoActionBar
  • Theme.MaterialComponents.DayNight.DarkActionBar

Tema y estilos

Debemos usar los estilos adecuadamente para que cuando se cambie a tema oscuro los elementos cambien de color, por ejemplo: la barra de acción de la app, el color del texto, los iconos…

Para que el modo oscuro afecte a la barra de la aplicación, en AppBarLayout debemos definir

android:theme="?attr/actionBarTheme" 

y el Toolbar definir

app:popupTheme="?attr/actionBarPopupTheme"

Todo junto así

    <com.google.android.material.appbar.AppBarLayout
        ...
        android:theme="?attr/actionBarTheme">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            ...
            app:popupTheme="?attr/actionBarPopupTheme"/>

Para elementos de texto

El color del texto ?android:attr/textColorPrimary
en el tema claro el texto será casi negro y en el tema oscuro será casi blanco, manteniendo estados de estilo inhabilitado.
Por defecto se establecen el color del texto, pero si queremos forzar en un elemento

android:textColor="?android:attr/textColorPrimary"

El color del texto secundario ?android:attr/textColorSecondary en el tema claro y oscuro se mostrará con un gris oscuro, manteniendo estados de estilo inhabilitado.

android:textColor="?android:attr/textColorSecondary"

Para elementos gráficos

El color de los iconos, aplicar el tinte en ?attr/colorControlNormal de esa forma se mantendrá la alteración del color en el estado inhabilitado

<vector
...
android:tint="?attr/colorControlNormal">

Personalización del Modo Oscuro

Si queremos una mayor personalización del tema claro y oscuro, podemos especificar por separado sus estilos

Definir el tema claro en values/styles.xml

    <style name="Theme.YourApp.Light" parent="Theme.MaterialComponents.Light">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <!-- your light theme attributes -->
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/primaryColor</item>
        <item name="colorPrimaryDark">@color/primaryDarkColor</item>
        <item name="colorAccent">@color/secondaryColor</item>
    </style>

Definir el tema oscuro en values/styles.xml

    <style name="Theme.YourApp.Dark" parent="Theme.MaterialComponents">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <!-- your dark theme attributes -->
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/primaryColor</item>
        <item name="colorPrimaryDark">@color/primaryDarkColor</item>
        <item name="colorAccent">@color/secondaryColor</item>
    </style>

Definir el tema oscuro en values-night/styles.xml

Para que se cargue un tema o otro dependiendo del modo oscuro, lo deberemos hacer con el sufijo -night en values-night/styles.xml definir que Theme.YourApp.DayNight herede del tema oscuro

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Theme.YourApp.DayNight" parent="Theme.YourApp.Dark" />
</resources>

Y en values/styles.xml definir que Theme.YourApp.DayNight herede del tema claro

<style name="Theme.YourApp.DayNight" parent="Theme.YourApp.Light" />

Luego en AndroidManifest.xml en las actividades que queremos implementar el modo oscuro definir el tema:

...
android:theme="@style/Theme.YourApp.DayNight"
...

Visualización

<style name="AppTheme.Light" parent="Theme.MaterialComponents.Light.Bridge">
<style name="AppTheme.Dark" parent="Theme.MaterialComponents.Bridge">
<style name="AppTheme.Light" parent="Theme.MaterialComponents.Light.DarkActionBar.Bridge">

Estilizar el ActionBar

El ActionBar de las aplicaciones puede ser de color opaco con la fuente de color blanco que normalmente se atribuye a DarkActionBar o bien que sea con fondo claro y letras negras.

El atributo de tema actionBarTheme es donde se especifica el estilo de la ActionBar
Propiedad actionBarPopupTheme es donde se especifica el estilo del menú contextual de la ActionBar

Barra superior oscura

<item name="actionBarTheme">@style/ThemeOverlay.MaterialComponents.Dark.ActionBar</item>
<item name="actionBarPopupTheme">@style/ThemeOverlay.MaterialComponents.Dark</item>

Barra superior clara

<item name="actionBarTheme">@style/ThemeOverlay.MaterialComponents.Light</item>
<item name="actionBarPopupTheme">@style/ThemeOverlay.MaterialComponents.Light</item>

Establecer un color a la barra superior
Para especificar un color se hace en elemento Toolbar y con la propiedad android:background:

<androidx.appcompat.widget.Toolbar
...
android:background="?attr/colorPrimary"
.../>

Ejemplo de tema personalizado

El tema personalizado heredera los colores de DarkActionBar, modificando el color de la ActionBar a Azul y el menú en oscuro para el tema claro, en el tema oscuro el color de la ActionBar será rojo

Los colores usados se obtienen de Lista de Colores Material Design

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.MaterialComponents.Light.Bridge">
        <!-- Customize your theme here. -->
    </style>

    <style name="AppTheme.Light" parent="Theme.MaterialComponents.Light.DarkActionBar.Bridge">
        <!-- your light theme attributes -->
        <item name="colorPrimary">@color/primaryColor</item>
        <item name="colorPrimaryDark">@color/primaryDarkColor</item>
        <item name="colorAccent">@color/secondaryColor</item>
        <item name="android:statusBarColor">@color/primaryColor</item>

        <item name="actionBarTheme">@style/ThemeOverlay.MaterialComponents.Dark.ActionBar</item>
        <item name="actionBarPopupTheme">@style/ThemeOverlay.MaterialComponents.Dark</item>
    </style>

    <style name="AppTheme.Dark" parent="Theme.MaterialComponents.Bridge">
        <!-- your dark theme attributes -->
        <item name="colorPrimary">@color/md_red_500</item>
        <item name="colorPrimaryDark">@color/md_red_700</item>
        <item name="colorAccent">@color/md_yellow_500</item>

        <item name="android:statusBarColor">?attr/colorPrimary</item>
        <item name="android:navigationBarColor">?attr/colorPrimarySurface</item>

    </style>

    <style name="AppTheme.DayNight" parent="AppTheme.Light" />

    <style name="AppTheme.DayNight.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

</resources>

Establecer el tema mediante código

Mediante código Kotlin podemos establecer como actuará el modo oscuro, podemos forzar que la aplicación se muestre con tema oscuro, claro, o dependiendo del sistema, se hace con la función appCompatDelegate.setDefaultNightMode(<valor_modo>)

  • AppCompatDelegate.MODE_NIGHT_NO para establecer el tema claro
  • AppCompatDelegate.MODE_NIGHT_YES para establecer el tema oscuro
  • AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM para establcer el tema predeterminado del sistema Android Q y en Android P cuando se activa el modo ahorro de batería

Lo ideal en la clase MyApplication que hereda de Application en init esteblecer

class MyApplication : Application() {
...
override fun onCreate() {
    super.onCreate()
    AppCompatDelegate.setDefaultNightMode(
        AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
    )
}
..
}
//AndroidManifest.xml, esteblecer <application
android:name=".MyApplication"

Obtener el estado de Modo Oscuro

Si queremos saber como está establecido el modo oscuro

fun isDarkTheme(activity: Activity): Boolean {
    return activity.resources.configuration.uiMode and
            Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
}

El siguiente enlace puedes obtener el ejemplo usado TestNavigationNavWithDarkTheme

Recursos adicionales

Publicado por Codelaby

Mobile DevDesigner

Un comentario en “Cómo implementar el modo oscuro en Kotlin para Android

  1. Hola, hice el modo oscuro utilizando el appCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO), ya que no deseo mayor personalización. Utilizando un botón switch para encender y apagar el modo oscuro, y quisiera saber cómo puedo guardar esta línea de código en SharedPreferences.
    Gracias

    Me gusta

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

A %d blogueros les gusta esto: