3

I am unable to change color of system navigation bar. In dark mode it ineed appears as transparent and its fine (ideally I would want it to be the same color as bottom navigation bar), but for light mode, its just straight white. I know that since Android 35, there is some kind of protection layer on it, but when set as transparent, it should allow me to change its color, or at least be transparent. Has anyone managed to make it work?

I haven't checked how it behaves on API >35.

When inspecting in layout inspector, indeed the box appears where system navigation bars are, but doesn't apply any color.

My background fills entire screen including system/navigation bars, when I set this color for example for Yellow, it is correctly drawn over navigation bars and status bars, but only when in dark mode, in light mode it is still white.

Below is my code for MainActivity:

class MainActivity : AppCompatActivity() {
    private val startupViewModel: StartupViewModel by viewModel()
    private val inAppUpdateManager: InAppUpdateManager by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        installSplashScreen()
        super.onCreate(savedInstanceState)

        var themeSettings by mutableStateOf(
            ThemeSettings(
                darkTheme = false,
                dynamicColor = false
            )
        )

        inAppUpdateManager.check(this)

        lifecycleScope.launch {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                combine(
                    isSystemInDarkTheme(),
                    startupViewModel.state
                ) { systemDark, uiState ->
                    ThemeSettings(
                        darkTheme = when (uiState.theme) {
                            Theme.SYSTEM -> systemDark
                            Theme.LIGHT -> false
                            Theme.DARK -> true
                        },
                        dynamicColor = uiState.dynamicColors
                    )
                }.onEach { themeSettings = it }
                    .map { it.darkTheme }
                    .distinctUntilChanged()
                    .collect { darkTheme ->
                        enableEdgeToEdge(
                            statusBarStyle = SystemBarStyle.auto(
                                lightScrim = Color.TRANSPARENT,
                                darkScrim = Color.TRANSPARENT,
                            ) { darkTheme },
                            navigationBarStyle = SystemBarStyle.auto(
                                lightScrim = Color.TRANSPARENT,
                                darkScrim = Color.TRANSPARENT,
                            ) { darkTheme }
                        )
                    }
            }
        }

        setContent {
            Theme(
                darkTheme = themeSettings.darkTheme,
                dynamicColor = themeSettings.dynamicColor
            ) {
                val state = startupViewModel.state.collectAsStateWithLifecycle()
                Background {
                    if (state.value.isLoading) {
                        StartupScreen(
                            showSkip = { state.value.showSkip },
                            onSkip = { startupViewModel.skipFetching() }
                        )
                    } else {
                        NavigationRoot(startDestination = state.value.startDestination)
                    }
                }
            }
        }
    }

    override fun onResume() {
        super.onResume()
        inAppUpdateManager.onResume(this)
    }
}

private val lightScrim = Color.argb(0xe6, 0xFF, 0xFF, 0x00)
private val darkScrim = Color.argb(0xe6, 0xFF, 0xFF, 0x00)

@Immutable
private data class ThemeSettings(
    val darkTheme: Boolean,
    val dynamicColor: Boolean,
)

And here is where i set my background:

@Composable
fun Background(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit,
) {
    val primary = MaterialTheme.colorScheme.surface
    val secondary = MaterialTheme.colorScheme.surfaceContainer

    val gradient = remember(primary, secondary) {
        Brush.verticalGradient(colors = listOf(primary, secondary))
    }

    Box(
        modifier = modifier.fillMaxSize()
    ) {
        Surface(
            color = Color.Transparent,
            modifier = Modifier
                .matchParentSize()
                .background(gradient)
        ) {
            CompositionLocalProvider(LocalAbsoluteTonalElevation provides 0.dp) {
                content()
            }
        }
      
        if (Build.VERSION.SDK_INT >= 35) {
            Box(
                modifier = Modifier
                    .align(Alignment.BottomCenter)
                    .fillMaxWidth()
                    .zIndex(Float.MAX_VALUE)
                    .windowInsetsBottomHeight(WindowInsets.navigationBars)
                    .background(MaterialTheme.colorScheme.surfaceContainer)
            )
        }
    }
}

@Edit 29.07.2025

Below is my working solution (thanks Mofe Ejegi), which gives brush background while also changing color of system navigation bar together with icons in them.

@Composable
fun Background(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit,
) {
    val primary = MaterialTheme.colorScheme.surface
    val secondary = MaterialTheme.colorScheme.surfaceContainer

    val gradient = remember(primary, secondary) {
        Brush.verticalGradient(colors = listOf(primary, secondary))
    }

    Box(
        modifier = modifier
            .fillMaxSize()
    ) {
        Surface(
            color = Color.Transparent,
            modifier = modifier
                .fillMaxSize()
                .background(gradient)
        ) {
            CompositionLocalProvider(LocalAbsoluteTonalElevation provides 0.dp) {
                content()
            }
        }
        Box(
            modifier = Modifier
                .align(Alignment.BottomCenter)
                .fillMaxWidth()
                .windowInsetsBottomHeight(WindowInsets.navigationBars)
                .background(MaterialTheme.colorScheme.surfaceContainer)
        )
    }
}
class MainActivity : AppCompatActivity() {
    private val startupViewModel: StartupViewModel by viewModel()
    private val inAppUpdateManager: InAppUpdateManager by inject()

    private lateinit var updateLauncher: ActivityResultLauncher<IntentSenderRequest>

    override fun onCreate(savedInstanceState: Bundle?) {
        installSplashScreen()
        var themeSettings by mutableStateOf(
            ThemeSettings(
                darkTheme = false,
                dynamicColor = false
            )
        )

        lifecycleScope.launch {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                combine(
                    isSystemInDarkTheme(),
                    startupViewModel.state
                ) { systemDark, uiState ->
                    ThemeSettings(
                        darkTheme = when (uiState.theme) {
                            Theme.SYSTEM -> systemDark
                            Theme.LIGHT -> false
                            Theme.DARK -> true
                        },
                        dynamicColor = uiState.dynamicColors
                    )
                }.onEach { themeSettings = it }
                    .map { it.darkTheme }
                    .distinctUntilChanged()
                    .collect { darkTheme ->
                        enableEdgeToEdge(
                            navigationBarStyle = if (darkTheme) {
                                SystemBarStyle.dark(
                                    scrim = Color.TRANSPARENT
                                )
                            } else {
                                SystemBarStyle.light(
                                    scrim = Color.TRANSPARENT,
                                    darkScrim = Color.TRANSPARENT
                                )
                            },
                            statusBarStyle = if (darkTheme) {
                                SystemBarStyle.dark(
                                    scrim = Color.TRANSPARENT
                                )
                            } else {
                                SystemBarStyle.light(
                                    scrim = Color.TRANSPARENT,
                                    darkScrim = Color.TRANSPARENT
                                )
                            }
                        )
                    }
            }
        }

        super.onCreate(savedInstanceState)

        updateLauncher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
            inAppUpdateManager.onUpdateResult(result, this)
        }
        inAppUpdateManager.setLauncher(updateLauncher, this)

        inAppUpdateManager.check(this)


        setContent {
            Theme(
                darkTheme = themeSettings.darkTheme,
                dynamicColor = themeSettings.dynamicColor
            ) {
                val state = startupViewModel.state.collectAsStateWithLifecycle()
                Background {
                    if (state.value.isLoading) {
                        StartupScreen(
                            showSkip = { state.value.showSkip },
                            onSkip = { startupViewModel.skipFetching() }
                        )
                    } else {
                        NavigationRoot(startDestination = state.value.startDestination)
                    }
                }
            }
        }
    }

    override fun onResume() {
        super.onResume()
        inAppUpdateManager.onResume(this)
    }
}

@Immutable
private data class ThemeSettings(
    val darkTheme: Boolean,
    val dynamicColor: Boolean,
)

2 Answers 2

2
+150

So prior to Android API 35, the way to do this was to call

window.navigationBarColor = yourNavBarColor.toArgb()

However, window.setNavigationBarColor() is deprecated from API 35+.
Since your approach doesn't reference that method, I assume you already know this.

So you're already on the right track to achieving this in apps compiled with API 35 or higher.
All that's left is just a few minor tweaks.

In your MainActivity.kt, add the enableEdgeToEdge function to create a transparent scrim for the System navigation bar:

class MainActivity : AppCompatActivity() {
    private val startupViewModel: StartupViewModel by viewModel()
    private val inAppUpdateManager: InAppUpdateManager by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        installSplashScreen()
        super.onCreate(savedInstanceState)

        // Force the exact color by disabling automatic theming
        enableEdgeToEdge(
            navigationBarStyle = SystemBarStyle.dark(
                scrim = android.graphics.Color.TRANSPARENT
            )
        )
        ...
    }
    
    ...
}

And in your Background Composable, you set the system navigation bar background with the appropriate height and color.

@Composable
fun Background(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit,
) {
    val navigationBarColor = Color.Yellow

    Box(
        modifier = modifier.fillMaxSize()
    ) {
        Surface(
            color = Color.White,
            modifier = Modifier.matchParentSize()
        ) {
            CompositionLocalProvider(LocalAbsoluteTonalElevation provides 0.dp) {
                content()
            }
        }

        // Draw yellow background behind WindowInsets.Type.navigationBars()
        Box(
            modifier = Modifier
                .align(Alignment.BottomCenter)
                .fillMaxWidth()
                .windowInsetsBottomHeight(WindowInsets.navigationBars)
                .background(navigationBarColor) // Your exact yellow color
        )
    }
}

NOTE:

  • There's no need to perform the version check anymore, this works across multiple API levels. I tested API 30 and upwards, but it should also work for API 29.

  • I used SystemBarStyle.dark so I can consistently get the required color. SystemBarStyle.auto seems to create some light/dark variants of the color depending on the theme.

  • On API 30 - 34, using SystemBarStyle.dark provided white navigation icons and SystemBarStyle.light gave dark nav icons. On API 35, it had no effect and was only dependent on the theme.

Sign up to request clarification or add additional context in comments.

1 Comment

In my implementation I had to use both SystemBarStyle.dark and SystemBarStyle.light with transparent colors, as if I used dark only, then on light theme icons in system navigation bar were barerly visible. Apart from that, everything works correctly, at least for API 35+, I even managed to have gradient working!
-1

In Android 14+, when gesture navigation is enabled, a protection area is rendered behind the navigation bar. In light mode, this area appears white and is not affected by transparent system bar settings unless you explicitly overlay it with your own UI.

Even if you're using SystemBarStyle.auto() with Color.TRANSPARENT, Android still draws this white background unless you render a view manually over it.

You're already adding a Box with WindowInsets.navigationBars at the bottom, which is the correct approach. But currently, you render it only for API >= 35, and it uses MaterialTheme.colorScheme.surfaceContainer, which might be very close to white in light mode.

Here’s what I suggest:

1. Render that bottom box for all API versions, not just API 35. This ensures consistency.

Box(
    modifier = Modifier
        .align(Alignment.BottomCenter)
        .fillMaxWidth()
        .zIndex(Float.MAX_VALUE)
        .windowInsetsBottomHeight(WindowInsets.navigationBars)
        .background(MaterialTheme.colorScheme.surface)
)

2. Make sure the color you're applying isn't just white.

In light mode, surfaceContainer might look white, which gives the illusion that it hasn’t changed. Try setting it to a custom color (like yellow or light gray) to verify behavior.

3. Double-check your theme.

Make sure MaterialTheme.colorScheme is returning the expected colors. Sometimes in light mode, if dynamicColor = true, it might override with system default tones.

If needed, you can also manually override the system navigation bar color in your onCreate() with:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    window.setDecorFitsSystemWindows(false)
    window.navigationBarColor = Color.TRANSPARENT
    window.insetsController?.setSystemBarsAppearance(
        WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS,
        WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS
    )
}

This works well with View-based apps. In Compose, you're better off layering a Box on top of the insets area like you're already doing.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.