Updating list during onDragStart breaks drag & drop detection
I do following:
- I have a list with items and expandable folders
- if the user drags a folder, I hide all items outside of folders and also collapse all folders
- this has the side effect that the list animates as soon as the drag starts
Result
- the items are not placed correctly initially (see the space on top in the video, the first not dragged folder should animate all the way up)
- dragging around does not lead to
onMovecallbacks until I move the item nearly all the way up, only afterwards theonMoveis working again - the filtering of the list during the drag event should initially trigger
onMovebecause even though I do not drag the item the item in the list is animated up and therefore I move the dragged item down relatively event though I do not move my finger.
Examples
Video 1:
- folder 1 is dragged, folder 2 should move all the way up (it doesn't correctly)
- folder 2 move up far enough so that it should trigger an
onMoveevent, this does not happen
Video 2:
- folder 2 is dragged, folder 1 moves all the way up correctly
- dragging folder 1 above folder 2 works correctly
https://github.com/user-attachments/assets/63162819-8ed7-4a6d-8b87-8b92dd2cca3d
https://github.com/user-attachments/assets/ea02d7a8-c556-42fc-ac69-13b9bbd66ece
try using the new interface in #71 to detect click first, hide item, then start dragging
You probably assumed that I did not do that because of my second issue, but I'm already doing this.
I have a whole "wrapper" with the extra functionality on top of your library that looks like following and is used in the example above.
What I do
- I remember the dragged key in the ´draggableHandle::onDragStarted` callback
- then I update the list and hide items outside of my folders (I also collapse open folders, but I kept that out of the example video as the issue already happens with removing the items above the folders) => I do filter my data and provide a new filtered data set to my lazy column
- I do not get
onMoveevents that come from animating the list based on the new filtered data (video 1) and dragging around a little bit does not trigger the event either... - I also added an
onDragDoneevent to persist changes
Code
val LocalReorderableLazyListStateWrapper = staticCompositionLocalOf<ReorderableLazyListStateWrapper> { throw Exception("LocalReorderableLazyListState not initialised") }
@Composable
fun <T> Modifier.draggableHandleWrapper(
scope: ReorderableCollectionItemScope,
key: T,
enabled: Boolean = true,
interactionSource: MutableInteractionSource? = null,
onDragStarted: (startedPosition: Offset) -> Unit = {},
onDragStopped: () -> Unit = {},
dragGestureDetector: DragGestureDetector = DragGestureDetector.Press
): Modifier {
val reorderableLazyListStateWrapper = LocalReorderableLazyListStateWrapper.current
return with(scope) {
draggableHandle(
enabled = enabled,
interactionSource = interactionSource,
onDragStarted = {
reorderableLazyListStateWrapper.draggedItemKey.value = key
onDragStarted(it)
},
onDragStopped = {
reorderableLazyListStateWrapper.draggedItemKey.value = null
onDragStopped()
},
dragGestureDetector = dragGestureDetector
)
}
}
class ReorderableLazyListStateWrapper(
val state: ReorderableLazyListState,
val draggedItemKey: MutableState<Any?>
)
@Composable
fun ProviderReorderableLazyListStateWrapper(
wrapper: ReorderableLazyListStateWrapper,
content: @Composable () -> Unit
) {
CompositionLocalProvider(
LocalReorderableLazyListStateWrapper provides wrapper
) {
content()
}
}
@Composable
fun rememberReorderableLazyListState(
lazyListState: LazyListState,
scrollThresholdPadding: PaddingValues = PaddingValues(0.dp),
scrollThreshold: Dp = ReorderableLazyCollectionDefaults.ScrollThreshold,
onMove: suspend CoroutineScope.(from: LazyListItemInfo, to: LazyListItemInfo) -> Unit,
onDragStarted: (key: Any) -> Unit = {},
onDragStopped: () -> Unit = {},
onDragDone: suspend (from: Int, to: Int) -> Unit = { _, _ -> }
): ReorderableLazyListStateWrapper {
val draggedItemKey = remember { mutableStateOf<Any?>(null) }
val fromFirst = rememberSaveable { mutableStateOf<Int?>(null) }
val toLast = rememberSaveable { mutableStateOf<Int?>(null) }
val dragDropState = rememberReorderableLazyListState(
lazyListState = lazyListState,
scrollThresholdPadding = scrollThresholdPadding,
scrollThreshold = scrollThreshold,
//scroller = scroller,
onMove = { from, to ->
if (fromFirst.value == null) {
fromFirst.value = from.index
}
toLast.value = to.index
onMove(from, to)
}
)
LaunchedEffect(dragDropState.isAnyItemDragging) {
val from = fromFirst.value
val to = toLast.value
if (!dragDropState.isAnyItemDragging) {
fromFirst.value = null
toLast.value = null
if (from != null && to != null)
onDragDone(from, to)
}
}
LaunchedEffect(draggedItemKey.value) {
val key = draggedItemKey.value
if (key != null)
onDragStarted(key)
else
onDragStopped()
}
return ReorderableLazyListStateWrapper(dragDropState, draggedItemKey)
}
Any other ideas?
Btw, if you want to investigate that I made a demo and tried to simplify it as much as possible here: https://github.com/MFlisar/tests/tree/master/app/modules/test-drag-drop/src/commonMain/kotlin/com/michaelflisar/tests/dragdrop
I removed all the selection logic e.g..
Or maybe you see in issue there, just let me know