How to Include a Dark Theme in your Android App

For developers, creating an Android app is an easy process – designing an appealing UI is what’s actually difficult. That’s why we wrote the guide “How to Design an Android App UI That Doesn’t Suck”, for developers who need some inspiration in creating simple yet appealing UIs. In that guide we briefly mentioned adding a dark / night mode theme to your apps, and in this guide we will walk you through it.

Adding a dark / night mode theme to your app can be a great option for your users – it saves battery life, and is much easier on the eyes at night. A white background on black text, or any combination of ‘bright’ colors in your UI, is really stressful on the eyes, particularly at night. Thus, a dark background with lighter text is much less stressful and harmful to the eyes, which is why many popular apps have started including a dark theme – YouTube and Reddit most recently, although Facebook still hasn’t published theirs after promising one back in May.

If you’d like to easily add a toggleable dark mode to your Android app, follow this Appuals guide and comment if you run into any issues!

Creating the XML Attributes

First you need to create an XML attributes file that will handle the theming. This is much better than including two sets of resources in your APK for two different themes, and thus bloating your APK size, as Android can natively change colors on attributes through this XML.

So in your app’s ‘resources’ folder, create a file named attrs.xml and add these lines (these are styleable attributes):

<declare-styleable name="CTAppTheme">
      <attr name=”primaryTextColor” format=”color”/> 
      <attr name=”secondaryTextColor” format=”color”/> 
      <attr name=”dividerColor” format=”color”/>
      <attr name=”backgroundCardColor” format=”color”/> 
      <attr name=”iconPlaceholder” format=”integer”/>
</declare-styleable>

Prior to Lollipop we could not specify attributes for drawables, so we had to specify the resource IDs for drawables – however, this is no longer the case.

Adding the Styles

To make this work, we need to create two separate styles which will share the same base. The first will obviously be your primary ‘light’ theme, and the second will be your ‘dark’ theme.

<!-- Light base activity theme -->
<style name="ActivityTheme.Primary.Base.Light" parent="Theme.AppCompat.NoActionBar">
    <item name="primaryTextColor">#F4F4F6</item>
    <item name="secondaryTextColor">#96F4F4F6</item>
    <item name="backgroundCardColor">#FFFF</item>
    <item name="dividerColor">#F2F2F3</item>
    <item name="iconPlaceholder">@drawable/i_light_plholder</item>
</style>

<!-- Dark base activity theme -->
<style name="ActivityTheme.Primary.Base.Dark"    parent="Theme.AppCompat.NoActionBar">
    <item name="primaryTextColor">#33343B/item>
    <item name="secondaryTextColor">#8033343B</item>
    <item name="backgroundCardColor">#28292e</item>
    <item name="dividerColor">#F2F2F3</item>
    <item name="iconPlaceholder">@drawable/i_dark_pholder</item>
</style>

Here are the actual styles for a given activity:

<!-- Specific Feed activity theme -->
<style name="FeedActivityThemeLight" parent="ActivityTheme.Primary.Base.Light">
    <item name="android:windowBackground">#F4F4F6</item>
</style>

<style name="FeedActivityThemeDark" parent="ActivityTheme.Primary.Base.Dark">
    <item name="android:windowBackground">#33343B</item>
</style>

You need to specify one of these themes into the Manifest for the app’s activity, which in this case is FeedActivity.

Stylizing the Views

Here’s an example of stylizing your views – in this example, its for a “Page not found” redirect error.

<ImageView
           android:id="@+id/placeHolderAssetImageView"
           android:layout_width="100dp"
           android:layout_height="100dp"
           android:src="?attr/iconPlaceholder"
           android:layout_marginBottom="8dp" />

       <TextView
           android:id="@+id/placeHolderTitleTextView"
           android:text="Page not found."
           android:textSize="16dp"
           android:textColor="?attr/primaryTextColor"
           android:layout_margin="16dp" />

How to Enable Dynamic Theme Switching

The best and most efficient way to enable dynamic theme switching is to load the SharedPreference valued which is used to store the setting into the Application instance using the Singleton pattern, which means we won’t have to do it at the start of each Activity.

public class App extends Application {
 public static final String TAG = "App";

private boolean isNightModeEnabled = false;

@Override
 public void onCreate() {
 super.onCreate();

// We load the Night Mode state here
 SharedPreferences mPrefs =  PreferenceManager.getDefaultSharedPreferences(this);
 this.isNightModeEnabled = mPrefs.getBoolean(“NIGHT_MODE”, false);
 }

public boolean isNightModeEnabled() {
 return isNightModeEnabled;
 }

public void setIsNightModeEnabled(boolean isNightModeEnabled) {
 this.isNightModeEnabled = isNightModeEnabled;
 }
}

Because this instance is being launched before anything else, it allows you to call isNightModeEnabled() Boolean whenever you want, in any activity belonging to the app once it has been opened.

public final class FeedActivity extends AppCompatActivity {
 private final static String TAG = “FeedActivity”;

@Override
 protected void onCreate(Bundle savedInstanceState) {
    if (MyApplication.getInstance().isNightModeEnabled()) {
       setTheme(R.style.FeedActivityThemeDark);
    }
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_feed);
 }
}

However, if you want to apply a different theme than the one that is specified in the manifest file, it must be done before calling the parent onCreate() method.

Setting The State Using AppCompat

AppCompat is really useful for a wide range of things, and in this case, we’ll be using it to set the mode state. This means that you no longer need to use the app instance to create a cached state, thus cutting down on app cache bloat (albeit by a very tiny margin).

AppCompatDelegate
.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);

Here, you can either use:

  • AppCompatDelegate.MODE_NIGHT_YES
    AppCompatDelegate.MODE_NIGHT_NO
    AppCompatDelegate.MODE_NIGHT_AUTO

Retrieving The State

AppCompatDelegate.getDefaultNightMode();

public final class FeedActivity extends AppCompatActivity {
 private final static String TAG = “FeedActivity”;

@Override
 protected void onCreate(Bundle savedInstanceState) {
    if (AppCompatDelegate.getDefaultNightMode()
    ==AppCompatDelegate.MODE_NIGHT_YES) {
       setTheme(R.style.FeedActivityThemeDark);
    }
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_feed);
 }
}

That should be it – you can continue customizing and tweaking values to your liking, but this guide is a great introduction to get you started with including a native dark theme in your Android app.

ABOUT THE AUTHOR

Kamil Anwar


Kamil is a certified MCITP, CCNA (W), CCNA (S) and a former British Computer Society Member with over 9 years of experience Configuring, Deploying and Managing Switches, Firewalls and Domain Controllers also an old-school still active on FreeNode.