Reorderable icon indicating copy to clipboard operation
Reorderable copied to clipboard

Updating list during onDragStart breaks drag & drop detection

Open MFlisar opened this issue 1 month ago • 3 comments

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 onMove callbacks until I move the item nearly all the way up, only afterwards the onMove is working again
  • the filtering of the list during the drag event should initially trigger onMove because 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 onMove event, 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

MFlisar avatar Oct 31 '25 10:10 MFlisar

try using the new interface in #71 to detect click first, hide item, then start dragging

Calvin-LL avatar Oct 31 '25 14:10 Calvin-LL

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 onMove events 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 onDragDone event 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?

MFlisar avatar Nov 02 '25 09:11 MFlisar

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

MFlisar avatar Nov 02 '25 11:11 MFlisar