Search code examples
androidandroid-jetpack-composeandroid-jetpack-compose-material3

The place where ColorScheme is used in LazyRow cannot respond to its update and recomposition


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.

Screenshots here off on

The lazy row item cannot change their color to latest primary color.

Versions here

  • androidx-compose = "1.6.1"
  • androidx-compose-material3 = "1.2.0"
@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


Solution

  • 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)
                            )
                        }
                    }
                }
            }
        }
    }