0

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
        )
    }
}
Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
Joan P.
  • 2,368
  • 6
  • 30
  • 63

2 Answers2

1

You can use something like:

var cardColor by remember { mutableStateOf(Red)}

Card(
    //..
    border = BorderStroke(2.dp, cardColor),
    onClick = { cardColor = Blue }
) {
    Text(
        text = "name"
    )
}

If you want to handle a selected state you can use something like:

var selectedCard by remember { mutableStateOf(false) }
var cardColor = if (selectedCard) Red else Black

Card(
    border = BorderStroke(2.dp, cardColor),
    onClick = { selectedCard = !selectedCard }
) {
    Text(
        text = "name"
    )
}
Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
  • It worked for me. Thank you Gabriele for helping me again. Do you know by chance, how to reset the color from black back to red, if click another card? Each time I only want to have one card to selected. – Joan P. Aug 12 '22 at 14:31
  • @JoanP. You can use a ViewModel to store the selected state of the cards. Then just use the second example to use a different color if the card is selected or not. – Gabriele Mariotti Aug 12 '22 at 15:00
1

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
        )
    }
}
Thracian
  • 43,021
  • 16
  • 133
  • 222
  • Hey. Thank you for helping. I will try your approach too and get back to you. – Joan P. Aug 12 '22 at 14:36
  • Do you know by chance, how to reset the color from black back to red, if click another card? Each time I only want to have one card to selected. – Joan P. Aug 12 '22 at 14:38
  • 1
    Second one has this property by checking current color. but you can do it inside in onClick with first option either. Just check current color then set the one that is not set – Thracian Aug 12 '22 at 14:38
  • Sounds good. I'll try that out now, and I get back to you. – Joan P. Aug 12 '22 at 14:39
  • 1
    `val newColor = if(user.color == Color.Red) Color.Black else Color.Red`this line checks current color first – Thracian Aug 12 '22 at 14:39
  • You don't have to put color inside you data class by the way. Just put a boolean `selected` flag and change it when it's clicked and check `selected` status to change current color for border – Thracian Aug 12 '22 at 14:40
  • I accepted your answer, because it is more appropriate than what I want. Thank you. – Joan P. Aug 13 '22 at 11:47