Android dark mode tips
Lately dark mode is getting popular on Android apps. So i decided to share some of my own experience playing with dark mode.
DayNight
To implement dark mode you must extend from DayNight theme.
<style name="AppTheme" parent="@stle/Theme.AppCompat.Daynight>
.....
</style>
If you use material components, then extend from Theme.MaterialComponents.Daynight or Theme.MaterialComponents.Daynight.Bridge if still migrating to material components.
Resources
Before applying dark theme to app, you must add resources for dark theme. It is very simple, just create dark theme specific resources:
- values-night
- drawable-night
- etc…
Managing colors
Fastest way to implement colors for dark theme is just inverting existing color in light mode. For example we have white background in app for light theme, so in dark theme it will be black.
<!-- color palette in light theme(colors.xml in values) -->
<color name="white">#ffffff</color><!-- color palette in dark theme (colors.xml in values-night) -->
<color name="white">#252525</color>
Problem is that we now have white color in dark theme which is not white and it will be confusing and problematic in future to maintain. So my suggestion would be to not touch color palette and let it be only in single file in default theme. Make new colors which will encapsulate palette. Like this:
<!-- app background colors in light theme (colors.xml in values) -->
<color name="main_app_background_color">@color/white</color><!-- color palette -->
<color name="white">#ffffff</color>
<color name="black">#252525</color><!-- app background colors in dark theme (colors.xml in values-night -->
<color name="main_app_background_color">@color/black</color>
Now it makes much more sense and is not confusing anymore. In light theme we will have white background and in dark theme we will have black background.
Drawables
For png and webp resources there should be no issues, just throw them in to dark theme drawable folder.
If you use vectors in your app then you have 2 options.
- Light theme and dark theme vectors(one of each)
- Single vector with reference to specific color.
Here we have a plus icon vector.
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="14dp"
android:viewportHeight="14.0"
android:viewportWidth="14.0"
android:width="14dp">
<path
android:fillColor="#FFF"
android:pathData="M6,0v6H0v2h6v6h2V8h6V6H8V0z"/>
</vector>
If you change fillColor from color hash to refence to color folder. Like
android:fillColor="@color/plus"
Now we might have white plus in light theme and other color on dark theme if specified, so no need for multiple files.
If you have a case when you need to add 100+ vector icons, you might not have time to manually change fillColor for every icon. So for time being you can just add dark theme versions of them and in future delete them.
Though there is still an issue. Asset Studio only imports one svg at a time. So importing 100+ svgs would take some time.
There is a solution for it:
With this tool, you should not have problem importing 100+ svgs.
Switching themes in app
To switch themes call:
AppCompatDelegate.setDefaultNightMode(mode)
You can choose one of these options
- MODE_NIGHT_NO (Light)
- MODE_NIGHT_YES (Dark)
- MODE_NIGHT_AUTO_BATTERY (Set by battery saver)
- MODE_NIGHT_FOLLOW_SYSTEM (System default)
Also to get current default mode call:
AppCompatDelegate.getDefaultNightMode()
If you want to have dark theme options inside your app then you should do it like this:
- Save current night mode in shared preferences when changing theme
- Inside App class check for saved preference
- Call setDefaultNightMode
Configuration changes
When app theme is changed, it triggers configuration mode. Cause to apply theme app, activites must be restarted. Be careful so that you do not have crashes in your app because of this. Remember on Android 10 and higher phones you can change theme from anywhere inside your app. So handle your activity/fragment restoration accordingly.
Remember such things like FragmentStatePagerAdapter caching fragments, etc…
If you want to handle it yourself then in manifest add this:
<activity
android:name=".MyActivity"
android:configChanges="uiMode" />
And you can get night mode information from configuration like this:
val currentNightMode = configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
when (currentNightMode) {
Configuration.UI_MODE_NIGHT_NO -> {}
Configuration.UI_MODE_NIGHT_YES -> {}
}
But id suggest leave it to default behavior, because it is usually suggested to not override configurations.
All in all, i hope this can help someone. Good luck implementing dark mode in your app.