Why should I use the the .selectable modifier instead of .clickable for select one item in Compose LazyColumn?

Viewed 69

I have read the question Compose LazyColumn select one item.

I think that either Code A or Code B can do almost the same work.

Why should I use the the .selectable modifier instead of .clickable for select one item in Compose LazyColumn ?

Code A

   val items = listOf(Pair("A", 1), Pair("AA", 144), Pair("BA", 99))
    var selectedItem by remember {mutableStateOf(items[0])  }
    LazyColumn {
        this.items(items = items) {
            Row(modifier = Modifier.clickable{selectedItem = it}) {
                if (selectedItem == it) {
                    Text(it.first, color = Color.Red)
                } else {
                    Text(it.first)
                }
            }
        }
    }

Code B

val items = listOf(Pair("A", 1), Pair("AA", 144), Pair("BA", 99))
var selectedItem by remember{mutableStateOf("")}
LazyColumn {
    this.items(items = items) {
        Row(modifier = Modifier.selectable(
            selected = selectedItem == it.first,
            onClick = { selectedItem = it.first}
        )
        ) {
            if (selectedItem == it.first) {
                Text(it.first, color = Color.Red)
            } else {
                Text(it.first)
            }
        }
    }
}
1 Answers

Selectable is actually Modifier.clickable under the hood with Modifier.semantics for accessibility and testing purposes with selected parameter.

fun Modifier.selectable(
    selected: Boolean,
    interactionSource: MutableInteractionSource,
    indication: Indication?,
    enabled: Boolean = true,
    role: Role? = null,
    onClick: () -> Unit
) = inspectable(
    inspectorInfo = debugInspectorInfo {
        name = "selectable"
        properties["selected"] = selected
        properties["interactionSource"] = interactionSource
        properties["indication"] = indication
        properties["enabled"] = enabled
        properties["role"] = role
        properties["onClick"] = onClick
    },
    factory = {
        Modifier.clickable(
            enabled = enabled,
            role = role,
            interactionSource = interactionSource,
            indication = indication,
            onClick = onClick
        ).semantics {
            this.selected = selected
        }
    }
)

There is no difference in that example between using Modifier.clickable or Modifier.selectable.

I use Modifier.selectable with Modifier.selectableGroup to create RadioButton group which can also be done with Modifier.clickable but selectable feels like more appropriate.

Text("Selectable group with text")

val radioOptions = listOf("Calls", "Missed", "Friends")

val (selectedOption: String, onOptionSelected: (String) -> Unit) = remember {
    mutableStateOf(
        radioOptions[0]
    )
}
// Note that Modifier.selectableGroup() is essential to ensure correct accessibility behavior
Column(Modifier.selectableGroup()) {
    radioOptions.forEach { text ->
        Row(
            Modifier
                .fillMaxWidth()
                .height(56.dp)
                .selectable(
                    selected = (text == selectedOption),
                    onClick = { onOptionSelected(text) },
                    role = Role.RadioButton
                )
                .padding(horizontal = 16.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            RadioButton(
                selected = (text == selectedOption),
                onClick = null // null recommended for accessibility with screenreaders
            )
            Text(
                text = text,
                style = MaterialTheme.typography.body1.merge(),
                modifier = Modifier.padding(start = 16.dp)
            )
        }
    }
}
Related