androidkotlinandroid-jetpack-composeandroid-jetpack-compose-material3android-compose-card

How to change the border color of a card, on card click in Jetpack Compose?


I have list of users that is displayed in a lazy row. Each row is represented by a card. Each card has a red border. How can I change the border of the card from red to black, on card click?

Here is what I have tried:

LazyRow(
    modifier = Modifier.fillMaxWidth()
) {
    items(users) { user ->
        UserCard(
            name = user.name
        )
    }
}

And here is the card:

fun UserCard(
    name: String
) {
    Card(
        modifier = Modifier.fillMaxWidth()
        border = BorderStroke(2.dp, Color.Red),
        onClick = { ??? }
    ) {
        Text(
            text = name
        )
    }
}

Solution

  • If you want to have a stateful Composable you can do it by storing color inside a remember with MutableState and change it on click. However this will reset when you scroll back to same item because it will be recomposed when it's on screen again and have state which is not recommended. Official document about state-hoisting.

    fun UserCard(
        name: String
    ) {
    
        var color by remember {mutableStateOf(Color.Red)}
        Card(
            modifier = Modifier.fillMaxWidth()
            border = BorderStroke(2.dp, color),
            onClick = { color = Color.Black}
        ) {
            Text(
                text = name
            )
        }
    }
    

    If you wish to have stateless Composable you can do it via state-hoisting. Unlike the one above this one will have same color even if you scroll down and go back up when new items will be recomposed will existing border color

    data class User(val name: String, var color: Color = Color.Red)
    
    @Composable
    private fun BorderList() {
        val userList = remember {
            mutableStateListOf<User>().apply {
                repeat(100) {
                    add(User("User $it"))
                }
            }
        }
    
        LazyColumn {
            itemsIndexed(userList) { index, user ->
                UserCard(name = user.name, borderColor = user.color) {
    
                    val newColor = if(user.color == Color.Red) Color.Black else Color.Red
                    userList[index] = user.copy(color = newColor)
                }
            }
        }
    }
    
    @Composable
    fun UserCard(
        name: String,
        borderColor: Color,
        onColorChange: () -> Unit
    ) {
        Card(
            modifier = Modifier.fillMaxWidth(),
            border = BorderStroke(2.dp, borderColor),
            onClick = { onColorChange() }
        ) {
            Text(
                text = name
            )
        }
    }