Implementing dark theme on Android

Olivier Mercier
5 min readSep 23, 2019

Dark theme has lots of benefits for your users. It reduces battery usage, improve your app accessibility, and even for all user when using in a low light environment.

In the first part, we focus on the design aspect, pre-requisite for this project.

Now we assume this is out of the way and you are about to implement it.

The key steps are available on the official documentation, the goal of this article is to focus on a real life use case and some additional complementary tips.

A) An opportunity to streamline color related code

Make sure do don’t have to manage colors you don’t really need.

Over the years, some additional design specs, different people joining in. If some things were overlooked during code reviews or design phases, you might end up with many shades of blue for example.

All your colors are referenced from one single place

In our case we have a “color_official.xml” file with our official set of colors. Here is a subset below:

Streamline the code color references

Now, here are some code you should investigate in your app (right click, find usages), to find places where colors are used in your code instead of XML. This is a non-exhaustive list but probably covers many apps.

ContextCompat.getColor
Resources.getColor
android.graphics.Color
android.graphic.Paint.setColor

For each one of those code usages, make sure they reference the colors listed in your XML file. If a color isn’t listed, you will need to add it.

Streamline the images resources (xml and vector drawables)

You probably have a set of images or graphics that have hardcoded colors. It is a good opportunity to update them so they also reference your color palette.

fillColor is correctly referencing the app color palette in the selected area, but not in the last occurrence

Some of the things to look for (non-exhaustive)

android:color="#
android:fillColor="#
android:strokeColor="#
android:startColor="#
android:endColor="#

For some non vector images and use cases (when you could use an image as a color mask), using the “tint color” could be an option, so you wouldn’t need to ask your designer for another version.

B) Code update for dark theme

In TripIt case, we made the following changes:

  • Updated all the activities theme to extend
    Theme.AppCompat.DayNight or Theme.AppCompat.DayNight.NoActionBar

Another AndroidX theme is available and would be recommended at a later stage, but is currently not in a stable release.

(Note that activities which do not leverage the android support libraries, there would be significantly more work required, and in this case dark theme probably isn’t your priority, but rather a full migration to AndroidX).

  • In the Application class (the one registered in the manifest with <application android:name=”<YourApplicationClass>”) we added
override fun onCreate() {
super.onCreate()
AppCompatDelegate.setDefaultNightMode(
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
)

The value would depend on your use case. It could be interesting to add a user setting within your app to give the user more control. For a first iteration, we left it at the user default value given by the OS.

B) Introducing the new dark theme palette

In addition to our “values/color_official.xml”, we added a “values-night/color_official.xml”. We also added a big comment a the top of the files so that developers would easily know which version they are seeing.

Side by side light and dark theme color palette.

It is interesting to notice that some colors are also intentionally the same value in our case. In terms of color naming, you might find disturbing to have something called “tripit_utility_grey” which, in dark theme, wouldn’t actually be grey.

That is something worth a discussion with your team. In our case, we thought in most cases we would still talk about design colors for the app in light mode, and using the “tripit_utility_grey” naming gave us a more useful information than “tripit_utility”. Also, as part of the conversation, we understood that each color starting with “tripit_*” could potentially have 2 different values depending on the theme, so in the end it is not an issue for us.

At this point you should be able to run the app and see the changes!

TripIt seen in light and dark theme

C) Fine tuning

The reality is, you likely won’t get the perfect result right away. You should expect various fine tuning.

In our case, we found the following issues:

  • We needed to specify the status bar icon light value in our activities theme. Otherwise the notifications icon were black on top of something very dark.
<item name="android:windowLightStatusBar"    ns1:targetApi="m">@bool/is_light_status_bar</item>

And in values/boolean.xml file (opposite value for values-night/boolean.xml):

<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="is_light_status_bar">true</bool>
</resources>
  • When one specific color was enough in light mode for different elements, in dark mode the designers wanted to use different ones.
    In this case, we had to introduce a new color reference in both light and dark mode, and just use the same value for the entries in light mode
  • Google Maps: It didn’t look great out of the box since the default look was mostly designed for light mode, so we just applied a custom theme to it.
See https://mapstyle.withgoogle.com/ and https://developers.google.com/maps/documentation/android-sdk/styling for more info.
  • Google Places: the license has a requirement to use a different google logo image. We just added the other logo in a drawable-night/ folder
  • Third Party embedded components: Some UI we do not control didn’t support dark mode. When possible, we can disable dark mode for one specific activity. This part is still work in progress for us.
  • Images without transparency.
The train icon above was in WebP format and had a hardcoded white background color.

In this case, instead of using a new WebP file, we switched over to SVG (smaller file) and made sure it was actually transparent.

There is no way around testing the entire app to make sure things are ok overall! We used a feature branch internally so we had enough time to see the different screens and fix a few more things.

I hope this gives interesting pointers and ideas as you get started with this project. Good luck!

Follow me on Twitter or LinkedIn for futures articles!

--

--