How to Implement Day/Night Mode in Your Android App
2019-07-09 | 5 min read

How to Implement Day/Night Mode in Your Android App

Goran Luketic

Android Developer

Since Android Pie (9.0) introduced the dark/light (day/night) theme to its users, a lot of the most popular apps have welcomed this feature with open arms. And so did we. Considering we like trying out new things, we decided to give it a go and implement this feature into one of our apps too.

In the simplest way possible, I will describe the process of implementing the dark/light themes inside an app, using the AppCompat library from AndroidX libraries — if you want to do the same, don’t go anywhere.



Before we start, here’s a suggestion: in case you’re working on any new projects, it’s highly recommended you use the AndroidX libraries. Also, if you haven’t done it already, you should consider transferring your existing projects to AndroidX as well.

Now, let’s start

First, import the latest AppCompat from the AndroidX libraries:

implementation 'androidx.appcompat:appcompat:1.1.0-alpha05'
gistfile1.txt hosted with ❤ by GitHub


This version of AppCompat will bring new updates and bug fixes regarding the DayNight theme.
Then, change your theme to extend from “Theme.AppCompat.DayNight”. Here’s an example of ours:

<style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
gistfile1.txt hosted with ❤ by GitHub

Moving on to some coding

Change the current theme during the app startup (it’s recommended to set it inside the Android Application Class). From v1.1.0-aplha05 this method will apply changes to any running activities, but it will also be preserved across configuration changes so there will be no need to call recreate().

override fun onCreate() {
super.onCreate()
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.getDefaultNightMode())
}
gistfile1.txt hosted with ❤ by GitHub

These are the four modes we use inside our app for the theme selection:

MODE_NIGHT_NO — sets light/day theme
MODE_NIGHT_YES — sets dark/night theme
MODE_NIGHT_AUTO_BATTERY — changes to the dark theme if the device has the battery saver feature enabled. This mode is available from v1.1.0-alpha03.
MODE_NIGHT_FOLLOW_SYSTEM — uses system theme (introduced in Android 9 (Pie)
MODE_NIGHT_AUTO_TIME and MODE_NIGHT_AUTO — sets the theme depending on device time, but this mode is deprecated from v1.1.0-alpha03 so we don’t use it in our app.

Inside the app there’s a RadioGroup for switching between different themes:

private fun initThemeListener(){
themeGroup.setOnCheckedChangeListener { _, checkedId ->
when (checkedId) {
R.id.themeLight -> setTheme(AppCompatDelegate.MODE_NIGHT_NO, THEME_LIGHT)
R.id.themeDark -> setTheme(AppCompatDelegate.MODE_NIGHT_YES, THEME_DARK)
R.id.themeBattery -> setTheme(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY, THEME_BATTERY)
R.id.themeSystem -> setTheme(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM, THEME_SYSTEM)
}
}
}
private fun setTheme(themeMode: Int, prefsMode: Int) {
AppCompatDelegate.setDefaultNightMode(themeMode)
saveTheme(prefsMode)
}
gistfile1.txt hosted with ❤ by GitHub

This is what it looks like on the device:




Don’t forget that the system theme only supports the Android Pie (9.0) version, so you need to remove the system theme option from UI for previous Android versions.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
themeSystem.visibility = View.VISIBLE
} else {
themeSystem.visibility = View.GONE
}
gistfile1.txt hosted with ❤ by GitHub


Checking the current system theme

A snippet from our app:

when (resources.configuration.uiMode.and(Configuration.UI_MODE_NIGHT_MASK)) {
Configuration.UI_MODE_NIGHT_NO -> themeLight.isChecked = true
Configuration.UI_MODE_NIGHT_YES -> themeDark.isChecked = true
Configuration.UI_MODE_NIGHT_UNDEFINED -> themeLight.isChecked = true
}
gistfile1.txt hosted with ❤ by GitHub

Note that the configuration returnsonlyif there is a night or day theme (or undefined), but we don’t know if this is set by the system, battery or manually. So we use shared preferences to store the selected theme and to check the right checkbox. Check out the complete code:

when (getSavedTheme()) {
THEME_LIGHT -> themeLight.isChecked = true
THEME_DARK -> themeDark.isChecked = true
THEME_SYSTEM -> themeSystem.isChecked = true
THEME_BATTERY -> themeBattery.isChecked = true
THEME_UNDEFINED -> {
when (resources.configuration.uiMode.and(Configuration.UI_MODE_NIGHT_MASK)) {
Configuration.UI_MODE_NIGHT_NO -> themeLight.isChecked = true
Configuration.UI_MODE_NIGHT_YES -> themeDark.isChecked = true
Configuration.UI_MODE_NIGHT_UNDEFINED -> themeLight.isChecked = true
}
}
}
gistfile1.txt hosted with ❤ by GitHub

When the app runs for the first time, in shared preferences will be an undefined state. In this case, we’ll check if we have a theme from the configuration. If not, we are going to use the light theme by default.

More control? Yes, please!


If you want to use custom colors for the dark/light theme (E.g., a dark blue for the night and a light blue for the day theme), start by creating a folder “values-night” and override the colors file. Here you can also override styles.xml and use custom attributes if you want. This way, the app will always use colors from “values-night” when the dark theme is configured and vice versa. You can also provide alternate resources for night UI. For example, drawable-night.


You can find the complete source code here:
https://github.com/cobeisfresh/themes-sample-android

Recap

Implementing the dark/light theme isn’t that complicated, especially with the new AppCompat library. There can be some additional work if you want to have more control over colors and styles, but the key is in preparation. Also, my advice is, if you’re planning on implementing this inside your project, do it right away — before kicking off the project. 
Now let’s hear from you, have you already tried implementing it in one of your apps too?

Also, if you have any questions, feel free to contact us :)


I’m just going to leave this here

Like what you read?Go on, share it with friends!
ABOUT THE AUTHOR

Goran Luketic

Android Developer
Goran is an Android developer at COBE, working in Kotlin and Java. When he’s not busy with native Android apps, he likes to learn new stuff so currently, he is implementing some apps in Flutter. Besides software, he’s not afraid of resistors, semiconductors or transistors so he likes to build some hardware stuff, especially audio power amplifiers. Or when he is not into software or hardware, he likes to ride a bicycle and relax with his family.

Let's turn your idea into reality

Save money, time and energy and book the entire team today.