Implementing Dark Mode on Android
April 18, 2020
Here are a few steps needed to add Dark Theme support to your Android app. This is not a comprehensive guide and your milage may vary. Especially, as the APIs evolve.
TL;DR:
Dark theme applies to both the Android system UI and apps running on the device.
To implement support for Dark Mode, you’ll be dealing with AppCompatDelegate
. Be aware that it’s done differently on Android 10 (API level 29) and higher vs previous versions.
Android 10 and higher ->
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
Pre-Android 10 ->
AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY
Adding Dark Mode support typically involves dealing with AppTheme and keeping track of the current setting in SharedPreferences
. For details, read on:
Force Dark Mode
If you are only supporting Android 10, you may be able to force dark mode without having to deal with the above settings by simply opting-in in the Dark Mode in styles.xml:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="android:forceDarkAllowed">true</item>
And then overwriting the setting in widgets, if needed:
<Button android:forceDarkAllowed="false"
Besides the fact that it only works on Android 10, you will likely run into limitations down the road. So let’s look into a more comprehensive approach of implementing Dark Mode on API levels 14+.
First, revert the force dark mode change made above.
Material Gradle dependency
Material dependency is needed for day night theme material support:
implementation "com.google.android.material:material:$materialVersion"
AppTheme with Dark Mode support
Update your styles.xml to use one of the Material Day Night themes:
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
Style Day and Night resources differently
For widgets that should be presented differently in night mode, update res/values-night/styles.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="MyImage" parent="AppTheme"> <item name="android:src">@drawable/my_night_image"
Persist Night Mode setting
For supporting Android 10 and above, we need to map user input to one of the following:
AppCompatDelegate.MODE_NIGHT_NO
AppCompatDelegate.MODE_NIGHT_YES
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
Note, for API levels between 14 and 28,
AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY
is equivalent toAppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
,
Typically, you’d have a menu to let user make a choice. For instance:
Once you get user input, perform the following steps:
To restart all started Activities, call
AppCompatDelegate.setDefaultNightMode(dayNightMode)
Persist user selection in
SharedPreferences
Icons to handle Day Night Mode switching
Let any vector assets automatically adjust color based on android:tint
setting:
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:tint="?android:colorControlNormal"
Retain Dark Mode settings between app restarts
Every time the app starts, you need to read the SharedPreferences to honor Dark Theme user setting. You can do that in the Application class:
class MyApplication : Application() { override fun onCreate() { super.onCreate() val dayNightMode = // read from SharedPreferences AppCompatDelegate.setDefaultNightMode(dayNightMode) }
Manually handle config changes with uiMode
When the app’s theme changes, it triggers a uiMode
configuration change. This means that Activities will be automatically recreated.
If you don’t want to immediately respond to this change (for instance, when a video in your app is playing), you need to manually handle uiMode
in AndroidManifest.xml
for a specific Activity:
<activity android:name=".MyActivity" android:configChanges="uiMode" />
And override onConfigurationChanged()
in the Activity itself:
override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) val currentSystemMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK when (currentNightMode) { Configuration.UI_MODE_NIGHT_NO -> // Night mode is not active Configuration.UI_MODE_NIGHT_YES -> // Night mode is active } }
Check out Dark Theme for more details on supporting Dark Theme.