I am writing an Android app using Jetpack Compose. I have a Composable called MultiSelectGroup
which needs to modify and return a list of selections whenever a FilterChip
is clicked. Here is the code:
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
@Composable
fun MultiSelectGroup(
items: List<String>,
currentSelections: List<String>,
onSelectionsChanged: (List<String>) -> Unit,
) {
FlowRow {
items.forEach { item ->
FilterChip(
label = { Text(item) },
selected = currentSelections.contains(item),
onClick = {
val newSelectedChips = currentSelections.toMutableList().apply {
if (contains(item)) {
remove(item)
} else {
add(item)
}
}
onSelectionsChanged(newSelectedChips)
},
)
}
}
}
This component is currently called using the following code:
val allItems = remember { (1..6).map {"$it"} }
val selectedItems = remember {
mutableStateOf(emptyList<String>())
}
MultiSelectGroup(
items = allItems,
currentSelections = selectedItems,
onSelectionsChanged = { selectedItems.value = it },
)
The problem is that this approach seems to be fairly inefficient in terms of recompositions; every time a FilterChip
is clicked, all FilterChip
s are recomposed whether they change visually or not. My current understanding is that this is because the list is being re-set and, with List
being a more unstable data type, Compose decides to just re-render all components dependant on the List
rather than just the elements in that List
have changed.
I have already considered hoisting the "updating list" logic in the onClick
of each FilterChip
out of the component - however it seems sensible for this component to do this logic as this behaviour will always be the same and would only be duplicated each time MultiSelectGroup
is used. I have also tried to use combinations of mutableStateList
, key
and derivedStatedOf
but I'm yet to find a solution that works.
Is a component like this doomed to always recompose each of its children? Or is there a way to optimise the recompositions for this kind of view? Thanks in advance!