Reputation: 23
I'm trying to apply the edge to edge effect with Compose Material 3, but the system navigation bar is getting a darker color in some devices.
Composable theme:
@Composable
fun AppTheme(
systemUiController: SystemUiController = rememberSystemUiController(),
useDarkColors: Boolean = isSystemInDarkTheme(),
enableDynamicColors: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
enableDynamicColors && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ->
if (useDarkColors) dynamicDarkColorScheme(LocalContext.current)
else dynamicLightColorScheme(LocalContext.current)
useDarkColors -> darkColorScheme()
else -> lightColorScheme()
}
DisposableEffect(key1 = Unit) {
systemUiController.setSystemBarsColor(
color = Color.Transparent,
darkIcons = colorScheme.background.luminance() > 0.5
)
onDispose { }
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography(),
shapes = Shapes(),
content = content
)
}
In the code above I defined the bars as transparent, and that the icons should be dark only if the background color brightness is above a specific value.
Obs: SystemUiController
is from Accompanist System UI Controller library.
In themes.xml:
<style name="AppTheme" parent="android:Theme.Material.NoActionBar">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
Main composable content:
@OptIn(ExperimentalMaterial3Api::class)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
AppTheme {
Scaffold(
modifier = Modifier
.fillMaxSize()
.nestedScroll(connection = scrollBehavior.nestedScrollConnection),
topBar = {
CenterAlignedTopAppBar(
modifier = Modifier.fillMaxWidth(),
title = { Text(text = "App Bar") },
scrollBehavior = scrollBehavior
)
},
bottomBar = {
NavigationBar(modifier = Modifier.fillMaxWidth()) {
repeat(times = 3) { index ->
NavigationBarItem(
selected = index == 0,
onClick = { },
icon = {
Icon(
imageVector = Icons.Default.Home,
contentDescription = null
)
},
label = { Text(text = "Item $index") }
)
}
}
},
contentWindowInsets = WindowInsets.systemBars
) { contentPadding ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues = contentPadding),
contentPadding = PaddingValues(all = 16.dp),
verticalArrangement = Arrangement.spacedBy(space = 16.dp)
) {
items(count = 50) { index ->
Text(text = "Item $index")
}
}
}
}
}
}
}
Here is a simple example where the system navigation bar is getting darker.
The system navigation bar is transparent, but for some reason it is applying a darker effect because of the NavigationBar
. If we remove the NavigationBar
we will see that it normally follows the background color, regardless of whether it is a light or dark theme with dynamic colors or not.
As I commented at the start, this "problem" does not happen on all devices, on my physical device with Android 12 this problem only happens when is light theme, on the emulator with Android 13 the problem happens regardless of the theme.
Comparing with the Gmail app, for example, the system navigation bar remains the same color as the NavigationBar
, and this is the effect I would like to have, instead of having a darker effect applied over it...
Problem when using NavigationBar
:
Normal behavior without NavigationBar
Expected appearance with NavigationBar
(from Gmail app)
Upvotes: 2
Views: 1426
Reputation: 3159
This behavior happens because of a functionality that is present on API level 29 and higher.
From Window class:
Whether the system should ensure that the navigation bar has enough contrast when a fully transparent background is requested.
From System UI Controller library:
Whether the system is ensuring that the navigation bar has enough contrast when a fully transparent background is requested. Only has an affect when running on Android API 29+ devices.
If you take a look at the setSystemBarsColor
method of the System UI Controller library, you can see that there is the isNavigationBarContrastEnforced
to disable this feature (default is enabled).
Also, depending on the minimum version of API used, it may not be possible to use dark icons, so the method setSystemBarsColor
has transformColorForLightContent
parameter for this scenario, by default, the color that will be used in this case is a 30% opaque black.
With that, you would solve the problem as follows:
DisposableEffect(
key1 = systemUiController,
key2 = useDarkColors,
key3 = enableDynamicColors
) {
systemUiController.setSystemBarsColor(
color = Color.Transparent,
darkIcons = colorScheme.background.luminance() > 0.5,
isNavigationBarContrastEnforced = false,
// line below is optional, if you want to use any color
// other than default black with 30% opacity:
transformColorForLightContent = { Color.Black.copy(alpha = 0.7F) }
)
onDispose { }
}
It is important to note that DisposableEffect
needs to receive the key parameters of systemUiController
, useDarkColors
and enableDynamicColors
, because if any of these keys change, the effects should be reapplied.
Upvotes: 5