Drop Strategies¶
When a dragged item overlaps multiple drop targets, a drop strategy determines which target is considered "hovered." Compose DND provides several built-in strategies and allows you to create custom ones.
What Are Drop Strategies?¶
During a drag operation, the dragged item may visually overlap more than one drop target at the same time. The drop strategy is the algorithm that decides which single drop target should receive the onDragEnter, onDragExit, and onDrop callbacks.
Built-in Strategies¶
Surface (Default)¶
Selects the drop target with the largest absolute overlapping area (in pixels) with the dragged item, regardless of the target's own size. This is the default strategy and works well for most use cases.
Text(
text = "Hello",
modifier = Modifier.draggableItem(
key = "item-1",
data = "Hello",
state = dragAndDropState,
dropStrategy = DropStrategy.Surface,
draggableContent = {
Text(text = "Hello")
},
),
)
SurfacePercentage¶
Selects the drop target that has the highest percentage of its own surface covered by the dragged item.
Text(
text = "Hello",
modifier = Modifier.draggableItem(
key = "item-1",
data = "Hello",
state = dragAndDropState,
dropStrategy = DropStrategy.SurfacePercentage,
draggableContent = {
Text(text = "Hello")
},
),
)
The percentage is calculated as:
This means a smaller drop target that is fully covered by the dragged item is preferred over a larger target that is only partially covered. When all targets have the same size, it picks the same target as Surface.
CenterDistance¶
Selects the drop target whose center is closest to the center of the dragged item. This strategy ignores overlap area entirely and focuses on proximity.
Text(
text = "Hello",
modifier = Modifier.draggableItem(
key = "item-1",
data = "Hello",
state = dragAndDropState,
dropStrategy = DropStrategy.CenterDistance,
draggableContent = {
Text(text = "Hello")
},
),
)
Comparison¶
| Strategy | Algorithm | Best For |
|---|---|---|
Surface | Max absolute overlapping area | General purpose, lists and grids (default) |
SurfacePercentage | Max overlapping area / target area | Small targets next to large ones |
CenterDistance | Min distance between centers | Precise targeting, small targets |
Z-Index¶
All built-in strategies account for the zIndex property of drop targets. When drop targets overlap visually, the one with a higher zIndex takes priority:
Box(
modifier = Modifier
.dropTarget(
key = "background-target",
state = dragAndDropState,
zIndex = 0f, // Lower priority
onDrop = { /* ... */ },
)
) {
Box(
modifier = Modifier
.dropTarget(
key = "foreground-target",
state = dragAndDropState,
zIndex = 1f, // Higher priority
onDrop = { /* ... */ },
)
)
}
Usage with reorderableItem¶
Drop strategies also work with the reorderableItem modifier:
Card(
modifier = Modifier.reorderableItem(
key = item,
data = item,
state = dndState,
dropStrategy = DropStrategy.CenterDistance,
onDragEnter = { state ->
// Reorder logic
},
draggableContent = {
// Drag shadow content
},
),
) {
// ...
}
Custom Drop Strategy¶
You can create a custom drop strategy by implementing the DropStrategy interface:
object MyCustomStrategy : DropStrategy {
override fun <T> getHoveredDropTarget(
draggedItemTopLeft: Offset,
draggedItemSize: Size,
dropTargets: List<DropTargetState<T>>,
): DropTargetState<T>? {
// Your custom logic here
return dropTargets.firstOrNull()
}
}
Then use it: