I am trying custom material3 color scheme in my own application. But I found the components in lazy row content cannot auto recomposition when color scheme changed.
Here are example:
@Composable
fun Test() {
var dark by remember { mutableStateOf(false) }
MaterialTheme(
colorScheme = MaterialTheme.colorScheme.copy(primary = if (dark) Color.Black else Color.White)
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Button("change") {
dark = !dark
}
Box(
modifier = Modifier
.size(16.dp)
.background(MaterialTheme.colorScheme.primary)
)
LazyRow {
items(3) {
Box(
modifier = Modifier
.size(16.dp)
.background(MaterialTheme.colorScheme.primary)
)
}
}
}
}
}
I changed the color scheme instance and apply it with MaterialTheme lambda.
The lazy row item cannot change their color to latest primary color.
Versions here
@Composable
fun Button(
text: String,
modifier: Modifier = Modifier,
enabled: Boolean = true,
containerColor: Color = MaterialTheme.colorScheme.primary,
contentColor: Color = MaterialTheme.colorScheme.onPrimary,
disabledContainerColor: Color = containerColor.copy(alpha = 0.12f),
disabledContentColor: Color = containerColor.copy(alpha = 0.38f),
onClick: () -> Unit
) {
val spacing = LocalSpacing.current
Button(
shape = RoundedCornerShape(8.dp),
onClick = onClick,
enabled = enabled,
modifier = modifier,
colors = ButtonDefaults.buttonColors(
containerColor = containerColor,
contentColor = contentColor,
disabledContainerColor = disabledContainerColor,
disabledContentColor = disabledContentColor
)
) {
Text(
text = text.uppercase()
)
}
}
Update:
I checked it again, I found the issue will only appear in material3-adaptive: ListDetailPaneScaffold component listPane
and detailPane
block.
androidx.compose.material3:material3-adaptive:1.0.0-alpha06
You are right, problem is somewhere inside ListDetailPaneScaffold
, i think it's a bug and you should you some temporary workaround to solve this. I made two solutions, it's on you which one you will prefer:
Solution 1: Wrap every LazyRow
item with MaterialTheme
Now i can't really explain why is recomposition working with MaterialTheme
applied again, but it's working.
@Composable
fun Test() {
var dark by remember { mutableStateOf(false) }
val originalColorScheme = MaterialTheme.colorScheme
val colorScheme = remember(key1 = dark) {
originalColorScheme.copy(primary = if (dark) Color.Black else Color.White)
}
MaterialTheme(colorScheme = colorScheme) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Button("change") {
dark = !dark
}
Box(
modifier = Modifier
.size(16.dp)
.background(MaterialTheme.colorScheme.primary)
)
LazyRow {
items(3) {
//Wrap every item to make sure that item will recompose
MaterialTheme(
colorScheme = colorScheme
) {
Box(
modifier = Modifier
.size(16.dp)
.background(MaterialTheme.colorScheme.primary)
)
}
}
}
}
}
}
Solution 2: Use custom provider to provide colorScheme
Now items will be recomposed because composer knows that value of the LocalColorScheme
provider has changed, so underlying ui using it will be recomposed
val LocalColorScheme = compositionLocalOf { lightColorScheme() }
@Composable
fun Test() {
var dark by remember { mutableStateOf(false) }
val originalColorScheme = MaterialTheme.colorScheme
//Change ColorScheme based on dark
val colorScheme = remember(key1 = dark) {
originalColorScheme.copy(primary = if (dark) Color.Black else Color.White)
}
MaterialTheme(colorScheme = colorScheme) {
//Provides colors to underlying composable
CompositionLocalProvider(
LocalColorScheme provides colorScheme
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Button("change") {
dark = !dark
}
Box(
modifier = Modifier
.size(16.dp)
//Use LocalColorScheme instead of Material Theme
.background(LocalColorScheme.current.primary)
)
LazyRow {
items(3) {
Box(
modifier = Modifier
.size(16.dp)
//Use LocalColorScheme instead of Material Theme
.background(LocalColorScheme.current.primary)
)
}
}
}
}
}
}