Reputation: 85
I am pretty new to Kotlin. I have a BottomNavigation
wrapped in a Scaffold
. The tabs background color change works well. But, though the icons are actually white, it appears black on the app, though I set the selectedContentColor
to Color.White
.
And unselectedContentColor
to Color.White.copy(alpha = 0.7f)
.
It does not even show whether or not I am in one tab or the other. They all just remain the same.
This is the code in my MainActivity.kt:
var selectedTab by remember { mutableIntStateOf(0) }
val tabs = listOf("Home", "Media", "Videos", "Contact")
val icons = listOf(
painterResource(id = R.drawable.ic_home),
painterResource(id = R.drawable.ic_media_link),
painterResource(id = R.drawable.ic_video_library),
painterResource(id = R.drawable.ic_account_circle),
)
Scaffold(
bottomBar = {
BottomNavigation(
backgroundColor = Color(0xff6c247c),
contentColor = Color.White,
) {
tabs.forEachIndexed { index, title ->
BottomNavigationItem(
icon = { Icon(painter = icons[index], contentDescription = title) },
label = { Text(title) },
selected = selectedTab == index,
onClick = { selectedTab = index },
alwaysShowLabel = true,
selectedContentColor = Color.White,
unselectedContentColor = Color.White.copy(alpha = 0.7f),
)
}
}
}
) {
when (selectedTab) {
0 -> HomeView()
1 -> MediaView()
2 -> VideosView()
3 -> ContactView()
}
}
And my theme.xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.HSMApp" parent="android:Theme.Material.Light.NoActionBar" >
<item name="colorPrimary">@color/primaryColor</item>
<item name="colorPrimaryVariant">@color/primaryVariantColor</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/secondaryColor</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
I also have this is in my Theme.kt
package com.phela.hsmapp.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun HSMAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
What should I change?
Upvotes: 2
Views: 120
Reputation: 14800
As already hinted in the comments this is caused by mixing Compose components of Material 2 and Material 3. I would strongly advise to not use the answer of the linked question, though: It downgrades your Material 3 components to Material 2. Since you also use other Material 3 components this will inevitably result in further complications.
Instead remove all Material 2 components in your app and rely entirely on Material 3. Look through your gradle files (or version catalog if you use that) for any dependencies that start with androidx.compose.material:
. They provide the old Material 2 components. Remove them and look for alternatives that start with androidx.compose.material3:
.
In your example the BottomNavigation
and BottomNavigationItem
are part of androidx.compose.material:material
. Replace that dependency with the Material 3 equivalent androidx.compose.material3:material3
. The Compose components are largey similar but most of them differ in certain aspects. In your case this means that BottomNavigation
becomes NavigationBar
and BottomNavigationItem
becomes NavigationBarItem
. Also, the color parameters changed. With the new Material 3 components it will look like this:
NavigationBar(
containerColor = Color(0xff6c247c),
contentColor = Color.White,
) {
tabs.forEachIndexed { index, title ->
NavigationBarItem(
icon = { Icon(painter = icons[index], contentDescription = title) },
label = { Text(title) },
selected = selectedTab == index,
onClick = { selectedTab = index },
alwaysShowLabel = true,
colors = NavigationBarItemDefaults.colors().copy(
selectedIconColor = Color.White,
selectedTextColor = Color.White,
unselectedIconColor = Color.White.copy(alpha = 0.7f),
unselectedTextColor = Color.White.copy(alpha = 0.7f),
),
)
}
}
That said, with Material 3 you should use a theme as defined in your Theme.kt
. This way you you won't manually define the colors for each component, instead the components retrieve the colors they need from the color scheme defined in the theme. This will also automatically take care of changing colors when switching to dark mode.
Currently you only have primary
, secondary
and tertiary
defined. The other color variants are derived from these colors. You can also specify them explicitly, though:
surfaceContainer
for NavigationBar
's containerColor
onSecondaryContainer
for NavigationBarItem
's selectedIconColor
onSurface
for NavigationBarItem
's selectedTextColor
onSurfaceVariant
for NavigationBarItem
's unselectedIconColor
and unselectedTextColor
(The NavigationBar
's contentColor
isn't used anywhere so I omitted it.)
Your LightColorScheme
could be defined like this:
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40,
surfaceContainer = Color(0xff6c247c),
onSecondaryContainer = Color.White,
onSurface = Color.White,
onSurfaceVariant = Color.White.copy(alpha = 0.7f),
)
You should also define approrpiate colors for DarkColorScheme
that will be used when the user switches to dark mode.
You can now drop all colors from the navigation bar like this:
NavigationBar {
tabs.forEachIndexed { index, title ->
NavigationBarItem(
icon = { Icon(painter = icons[index], contentDescription = title) },
label = { Text(title) },
selected = selectedTab == index,
onClick = { selectedTab = index },
alwaysShowLabel = true,
)
}
}
Now, keep in mind that all the colors defined in the theme also affect other composables, not just the navigation bar. This is by design so the overall look of the app is consistent across all components. You might want to tweak the values to improve it. If you don't want to define all color values manually you can use the Material Theme Builder. It's a website you can use to automatically generate and download the Theme.kt
file with matching colors for some presets you define.
Having said all that you might want to consider replacing the entire Scaffold with a NavigationSuiteScaffold
. That builds upon NavigationBar
and NavigationBarItem
so it uses the same components under the hood and the same theme is used, but it also supports other kinds of navigation components, like a NavigationRail
(placed on the side) or a NavigationDrawer
(small bar at the side, can be drawn up). Which of these will be used is then decided during runtime depending on the available screen size as recommended in Navigation in Material 3.
Now, keep in mind that this is still experimental so the API may change in the future. As it is right now you could replace your entire Scaffold with this:
NavigationSuiteScaffold(
navigationSuiteItems = {
tabs.forEachIndexed { index, title ->
item(
icon = { Icon(painter = icons[index], contentDescription = title) },
label = { Text(title) },
selected = selectedTab == index,
onClick = { selectedTab = index },
alwaysShowLabel = true,
)
}
},
) {
Column {
when (selectedTab) {
0 -> HomeView()
1 -> MediaView()
2 -> VideosView()
3 -> ContactView()
}
}
}
You need to add the gradle dependency androidx.compose.material3:material3-adaptive-navigation-suite-android
.
Upvotes: 1