Drag Handle¶
By default, dragging can be initiated from anywhere within a DraggableItem or ReorderableItem. A drag handle restricts the drag gesture to a specific area within the item, such as a grip icon. This is useful when your item contains interactive elements like buttons or text fields that should not trigger a drag.
Overview¶
To use a drag handle:
- Set
dragAfterLongPress = falseon your item (this is the default). - Enable the drag handle on the item by setting
hasDragHandle = true. - Place a
DragHandlecomposable inside the item's content to define the draggable area.
Experimental API
The drag handle API is annotated with @ExperimentalDndApi. You must opt in with @OptIn(ExperimentalDndApi::class) to use it.
Using Drag Handle with ReorderableItem¶
@OptIn(ExperimentalDndApi::class)
@Composable
fun DragHandleExample() {
val reorderState = rememberReorderState<String>()
var items by remember {
mutableStateOf(listOf("Item 1", "Item 2", "Item 3"))
}
ReorderContainer(
state = reorderState,
) {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.fillMaxSize().padding(16.dp),
) {
items(items, key = { it }) { item ->
ReorderableItem(
state = reorderState,
key = item,
data = item,
hasDragHandle = true,
onDrop = {},
onDragEnter = { state ->
items = items.toMutableList().apply {
val index = indexOf(item)
if (index == -1) return@ReorderableItem
remove(state.data)
add(index, state.data)
}
},
modifier = Modifier.fillMaxWidth(),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.graphicsLayer {
alpha = if (isDragging) 0f else 1f
}
.fillMaxWidth()
.height(56.dp)
.padding(horizontal = 16.dp),
) {
// Only this area initiates the drag
DragHandle {
Icon(
imageVector = Icons.Default.DragHandle,
contentDescription = "Drag handle",
)
}
Spacer(modifier = Modifier.width(16.dp))
Text(text = item)
}
}
}
}
}
}
Using Drag Handle with DraggableItem¶
The drag handle also works with DraggableItem:
@OptIn(ExperimentalDndApi::class)
DraggableItem(
state = dragAndDropState,
key = "item-1",
data = "Hello",
hasDragHandle = true,
) {
Row(verticalAlignment = Alignment.CenterVertically) {
DragHandle {
Icon(
imageVector = Icons.Default.DragHandle,
contentDescription = "Drag handle",
)
}
Spacer(modifier = Modifier.width(8.dp))
Text("Drag me by the handle")
}
}
How It Works¶
When hasDragHandle = true:
- Touch events on the item body are not intercepted for drag gestures.
- Only touch events starting inside the
DragHandlecomposable will initiate a drag. - Other interactive elements (buttons, text fields, checkboxes) inside the item remain fully functional.
Tip
Drag handles are especially useful in reorderable lists where each item contains clickable elements. Without a drag handle, tapping a button might accidentally start a drag gesture.