Proper way of implementing full dark theme in Compose-only applications

703 Views Asked by At

I have a Compose-only application that gives users three options of theming:

  • System
  • Light
  • Dark

The theme is changed in Activity's onCreate() method by passing darkTheme argument to my app's theme Composable function.

If System is selected and Android system supports Dark mode (Android 10+), everything works fine.

But if Dark is selected, the dark theme defined in Compose is applied, however the system navigation bar at the bottom stays white.

Then I went to the res/values/themes.xml file and it looked like this:

<?xml version="1.0" encoding="utf-8"?>
<resources>

  <style name="Theme.ExampleApp" parent="android:Theme.Material.Light.NoActionBar" />
</resources>

So, I assume that the system navigation bar stays white, because in Compose the default theme is android:Theme.Material.Light.NoActionBar and we can't control its color from Compose code.

This is not a big problem for Android 10+ devices, because they can simply select System option, and then manually turn on/off the system Dark mode from within the Android.

But for the devices that don't support the system Dark mode, they navigation bar stays white, which looks terrible.

So, I did some searching and found a way to "fix" this (mainly from this article).

  1. First, I changed the parent theme in styles.xml file to the following:
<?xml version="1.0" encoding="utf-8"?>
<resources>

  <style name="Theme.ExampleApp" parent="Theme.AppCompat.DayNight" />
</resources>
  1. This doesn't work unless your activity extends AppCompatActivity. Default Compose-only applications have their activity extend ComponentActivity. So:
class MainActivity : AppCompatActivity()
  1. Then I switch to so-called "Night mode" using the following functions:
// For SDK <= 30
AppCompatDelegate.setDefaultNightMode()

// For SDK >= 31
uiManager.setApplicationNightMode()

And it works!

However, I have some concerns and questions regarding this method.

  1. Is it OK to use the Theme.AppCompat.DayNight style for Compose-only application? It seems really old and I wonder if it breaks something in Compose.

  2. What are the drawbacks of using AppCompatActivity instead of ComponentActivity in Compose-only applications? I don't use Fragments, so I don't really need it, but maybe again, it breaks something in Compose? Does it increase .apk size?

  3. I don't know which parameters to call the functions setDefaultNightMode() and setApplicationNightMode() with for "Follow the system theme" mode. For the first one there is MODE_NIGHT_FOLLOW_SYSTEM, but the second function doesn't have it, and only has MODE_NIGHT_AUTO and MODE_NIGHT_CUSTOM. They both say that they switch to "Night mode" based on the current time, which I don't really want. For Android 10+ I don't want them to break their native Dark mode functionality.

1

There are 1 best solutions below

0
AndrazP On

If system status or navigation bars are not changing with in-app set theme, make sure to:

  1. Remove properties like android:windowLightStatusBar and android:windowLightNavigationBar from themes.xml.
  2. Set isAppearanceLightStatusBars and isAppearanceLightNavigationBars on WindowInsetsControllerCompat in your Compose theme.
@Composable
fun AppTheme(
   useDarkTheme: Boolean =  isSystemInDarkTheme(),
   content: @Composable () -> Unit
) {
 
 // color scheme selection code

 // Add primary status bar color from chosen color scheme.
 val view = LocalView.current
 if (!view.isInEditMode) {
    SideEffect {
        val window = (view.context as Activity).window
        window.statusBarColor = colors.primary.toArgb()
        WindowCompat.getInsetsController(window, view).apply {
            isAppearanceLightStatusBars = !isDarkTheme
            isAppearanceLightNavigationBars = !isDarkTheme
        }
    }
 }
   
 MaterialTheme(
   colorScheme = colors,
    content = content
 )
}