I have a list of selectable items and I want to be able to reorder only selected items. So I have a composable like this:
@Composable
private fun EmployeeSelectionDialog(
employees: List<EmployeeEntity>,
selectedEmployees: List<EmployeeEntity>,
onDismissRequest: () -> Unit,
onClick: (EmployeeEntity, Boolean) -> Unit,
onReordered: (List<EmployeeEntity>) -> Unit
) {
var selectedEmployeesMutable = remember(selectedEmployees) {
println("Selected employees: ${selectedEmployees.joinToString { it.id.toString() }}")
selectedEmployees
}
val reorderState = rememberReorderableLazyListState(
onMove = { from, to ->
selectedEmployeesMutable = selectedEmployeesMutable.toMutableList().apply {
add(to.index, removeAt(from.index))
}
},
canDragOver = { draggedOver: ItemPosition, _: ItemPosition ->
(draggedOver.key in selectedEmployeesMutable.map { it.id }).also {
println("Can drag over ${draggedOver.key}: $it (against [${selectedEmployeesMutable.joinToString { it.id.toString() }}])")
}
},
onDragEnd = { _, _ ->
onReordered(selectedEmployeesMutable)
}
)
val unselectedEmployees by remember(employees, selectedEmployees) {
mutableStateOf(employees.filter { it !in selectedEmployees })
}
@Composable
fun LazyItemScope.EmployeeItem(
employee: EmployeeEntity,
isDragging: Boolean,
) {
// Composing what I need and applying the detectReorder modifier if selected
}
LazyColumn(
modifier = Modifier
.width(400.dp)
.reorderable(reorderState),
state = reorderState.listState
) {
items(
items = selectedEmployeesMutable,
key = { it.id }
) { employee ->
ReorderableItem(reorderState, key = employee.id) { isDragging ->
EmployeeItem(
employee = employee,
isDragging = isDragging
)
}
}
if (selectedEmployees.isNotEmpty()) {
item {
Divider()
}
}
items(
items = unselectedEmployees,
key = { it.id }
) { employee ->
EmployeeItem(
employee = employee,
isDragging = false
)
}
}
}
}
onClick and onReordered functions end up making a DB write to mark items selected or change their order respectively, and this data then gets passed back into composition through Flows.
The problem is that the canDragOver factory seems to capture what it needs in a closure and be remembered along with its ReorderableLazyListState, not caring that selectedEmployeesMutable was changed. So if I check some checkboxes and then try to reorder selected items, my console will look like this:
Selected employees:
Selected employees: 42
Selected employees: 42, 26
Can drag over 26: false (against [])
Can drag over 26: false (against [])
This issue is even worse for un-checking some items since selectedEmployeesMutable captured in a closure retains wrong items, and this can cause IndexOutOfBoundsExceptions.
So are my assumptions correct and can I fix this on my end, or is the fix needed in the library? Thanks.
I have a list of selectable items and I want to be able to reorder only selected items. So I have a composable like this:
onClick
andonReordered
functions end up making a DB write to mark items selected or change their order respectively, and this data then gets passed back into composition throughFlow
s.The problem is that the
canDragOver
factory seems to capture what it needs in a closure and be remembered along with itsReorderableLazyListState
, not caring thatselectedEmployeesMutable
was changed. So if I check some checkboxes and then try to reorder selected items, my console will look like this:This issue is even worse for un-checking some items since
selectedEmployeesMutable
captured in a closure retains wrong items, and this can causeIndexOutOfBoundsException
s.So are my assumptions correct and can I fix this on my end, or is the fix needed in the library? Thanks.