Implementing Dark Mode on Android — part one

Lessons learned from a single Activity, multi-flavored, dynamically themed Application

Photo by Kelly Sikkema on Unsplash

My Baseline

The main newspaper app, Expressen, includes articles from all four brands (Expressen, GT, Kvp, Sport) and each brand has its theme. When entering an article from a specific brand, the entire app should use that theme. There’s also a section that isn’t a separate brand, but a section with a separate theme. Each brand also has its app, so four flavors. Thankfully they all default to a light background and dark text for the content. But the headers differ.. some are light and some are dark.

Colors; got to get them all!

I had the pleasure of being given a specification of all the colors that should exist in the app. So I knew that if any color wasn’t in the specification it should be removed. They also had a standardized naming on them c1, c2 and so on.

Make your attributes

Next up was to have the proper attributes for colors, to be able to utilize themes in android properly we should use style attributes. There are a few we often use that is built into android and the full list is available here.

The theme

Then in my theme.xml I declared a base theme for the main app, this theme should extend Theme.MaterialComponents.DayNight. In my base theme, I set all the style attributes to the correct colors from my palette. But I chose to complicate (or separate) it all one more step. Since I don’t want to use the c1, c2 colors explicitly since they are specified by our UX, and might be subject to change. So I added new colors and prefixed their names with mode_. This way I can have one palette file for dark mode and one for light mode specifying the color mode_content_background to the correct color from the specification for that mode.

Let’s look at the content background color full chain to clarify

// file: values/palette_spec.xml 
// Here I specify all colors allowed
<color name="c1">#212121</color> // almost black
<color name="c5">#FAFAFA</color> // almost white
// file: values/palette.xml
// And the light mode specific palette
<color name="mode_color_background">@color/c5</color>
// file: values-night/palette.xml
// And the light mode specific palette
<color name="mode_color_background">@color/c1</color>
// file: values/attrs.xml
// The styled attribute to be used by the views
<attr name="themeContentBackground" format="color" />
// file: values/theme.xml
// And in our theme that connect our attributes with colors
<item name="themeContentBackground">
@color/mode_content_background
</item>

Make our views depend on the theme

Now that we have the setup, we need to do some implementation of it as well. My app is still pink and purple and screaming. So we need to change all hardcoded colors into using styled attributes instead. So for every #xxxxxx and @color we have to change it to the corresponding ?styledAttribute. And I really mean all of them! They lurk in the darkest dungeons, like a <selector> or a <vector> shape. By doing this we can remove our old colors one by one. Or if you don’t care about visual feedback you can also just remove all your old colors from the top and just replace their references with the help of compiler errors.

Done?

And that’s actually my preferred setup. Simple as that, we should be done! All views use styled attributes, our themes define what colors they should be, our colors exist in both dark and light mode, and the theme is specified by the manifest. This is how I would build dark mode from scratch.

Lead Developer at Qvik, Coach, Agile Thinker, GDG Lead.