Supporting Dark mode in Android apps

vamsi krishna.s
3 min readApr 21, 2020

Dark mode is available in Android 10 ( Api level 29 ) and above. Dark mode has several benefits:

  • Increases battery life on smart phones with OLED screens
  • Improves visibility for users with low vision and those who are sensitive to bright light
  • Makes the usage of your phone eye-friendly in a low light environment

Dark Mode is opt-in

Technically speaking, you can choose whether you want to support Dark Mode in your app or not. Dark mode can be enabled in two ways:

  • Enable the new Force Dark feature by setting android:forceDarkAllowed=”true” in the app’s theme. This is a quick way for developers to enable Dark Mode in their apps. When you opt-in for Force Dark, you should should make sure to test your app thoroughly and exclude any views if required.
  • We can disable Force Dark on certain views with the android:forceDarkAllowed layout attribute or with setForceDarkAllowed().
  • Make your app’s theme extend a DayNight theme. Set your theme to extend Theme.AppCompat.DayNight in case of AppCompat usage and Theme.MaterialComponents.DayNight in case of MaterialComponents usage. It's backward compatible till API 14

Extending DayNight theme is considered as the the recommended approach out of the above two approaches because of the support towards backward compatibility. Force Dark may only be used as a quick temporary solution for rapid development iteration.

We can enable Dark Mode at three levels:

System Level

At system level, there are three ways to enable Dark Mode

  • Toggling Dark theme in system settings (Settings > Display > Dark Theme)
  • Enabling Night Mode in developer options (Settings > System > Developer Options > Night Mode) : Available in Android P only (API 28)
  • In Pixel devices , enabling Battery Saver mode enables Dark Mode. Not all OEMs may support this behaviour

Once any of the above settings are enabled, your application gets a callback to Application.onConfigurationChange. Now, it is up to the application to either follow it or override with Application level/Activity level setting.

If a theme is changed either by system setting or through AppCompat, a uiMode configuration change is triggered.

An app can handle the implementation of Dark Mode configuration change itself by declaring that each activity can handle the uiMode configuration change as below:

<activity android:name=".MyActivity" android:configChanges="uiMode" />

Application level

As a developer you can let user choose the theme for your app (overriding the system setting). It has to be controlled using AppCompatDelegate.setDefaultNightMode

Allowed options for api setDefaultNightMode are

  • MODE_NIGHT_AUTO_BATTERY — Night mode which uses a dark mode when the system’s ‘Battery Saver’ feature is enabled, otherwise it uses a ‘light mode’.
  • MODE_NIGHT_FOLLOW_SYSTEM — Mode which uses the system’s night mode setting to determine if it is night or not.
  • MODE_NIGHT_NO — Night mode which always uses a light mode, enabling notnight qualified resources regardless of the time.
  • MODE_NIGHT_YES — Night mode which always uses a dark mode, enabling night qualified resources regardless of the time.

Activity Level

This is similar to the Application Setting. At this level, a developer can decide at the granularity of an Activity. It has to be controlled using setLocalNightMode

In addition to the modes allowed for setDefaultNightMode api as listed in above point, MODE_NIGHT_UNSPECIFIED is primarily used at Activity level.

MODE_NIGHT_UNSPECIFIED is an unspecified mode for night mode. This is used to allow the default night mode to be used. If both the default and local night modes are set to this value, then the default value of MODE_NIGHT_FOLLOW_SYSTEM is applied.

How to programatically find what is the current theme ?

We can run the below code snippet to find what the current theme is :

val currentNightMode = configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
when (currentNightMode) {
Configuration.UI_MODE_NIGHT_NO -> {} // Night mode is not active, we're using the light theme
Configuration.UI_MODE_NIGHT_YES -> {} // Night mode is active, we're using dark theme
}

Rules of Thumb

  • Reuse semantically named theme attributes whenever you can (in layouts, drawables, color lists, etc.) which can have different values when running under the dark or light theme. For instance, you can use ?attr/colorSurface, ?attr/colorOnBackground or ?attr/colorPrimary from the latest material palette to name a few;
  • Provide alternative versions for other things using -night qualifier;

References

--

--