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,
)