Jetpack Compose supports drag and drop with two modifiers:
dragAndDropSource
: Specifies a composable as the starting point of the drag gesturedragAndDropTarget
: Specifies a composable that accepts the dropped data
For example, to enable users to drag an image in your app, create an image composable
and add the dragAndDropSource
modifier. To set up a drop target, create another
image composable and add the dragAndDropTarget
modifier.
The modifiers can be applied to multiple drag sources and multiple drop targets.
The modifiers enable apps to share data between two or more composables
using ClipData
, which is interoperable with View
implementations.
Start a drag event
To enable drag events inside a component, add the dragAndDropSource
modifier,
which takes a suspending function as a parameter. The function defines the user
interaction that starts the drag operation. The dragAndDropSource
modifier
waits until it receives a pointer input event and then executes the lambda
passed to the event handler. Use the lambda to detect a variety of input events,
for example, taps or long presses. For more information, see Pointer
input in Compose.
The pointer input event is usually a long press implemented as follows:
Modifier.dragAndDropSource { detectTapGestures(onLongPress = { // Transfer data here. }) }
To start a drag-and-drop session, call the startTransfer()
function.
Inside this scope, use DragAndDropTransferData
to represent the
transferable data. The data can be a remote URI, rich text data on the
clipboard, a local file, or more, but they all need to be wrapped in a
ClipData
object. Provide plain text, for example, as follows:
Modifier.dragAndDropSource { detectTapGestures(onLongPress = { startTransfer( DragAndDropTransferData( ClipData.newPlainText( "image Url", url ) ) ) }) }
To allow the drag action to cross the borders of the app, the
DragAndDropTransferData
constructor accepts a flags
argument. In the
following example, the DRAG_FLAG_GLOBAL
constant specifies that data can
be dragged from one app into another:
Modifier.dragAndDropSource { detectTapGestures(onLongPress = { startTransfer( DragAndDropTransferData( ClipData.newPlainText( "image Url", url ), flags = View.DRAG_FLAG_GLOBAL ) ) }) }
DragAndDropTransferData
accepts flags supported by the Android View system. See
the list of View constants for an exhaustive list of available flags.
Receive drop data
Assign the dragAndDropTarget
modifier to a composable to enable the composable
to receive drag-and-drop events. The modifier has two parameters: the first
acts as a filter and specifies the kind of data the modifier can accept, and the
second delivers the data in a callback.
Note that the callback instance should be remembered. The following snippet shows how to remember the callback:
val callback = remember { object : DragAndDropTarget { override fun onDrop(event: DragAndDropEvent): Boolean { // Parse received data return true } } }
The next snippet demonstrates how to handle dropped plain text:
Modifier.dragAndDropTarget( shouldStartDragAndDrop = { event -> event.mimeTypes().contains(ClipDescription.MIMETYPE_TEXT_PLAIN) }, target = callback )
The callback function should return true
if the event is consumed, or false
if the event is refused and does not propagate to the parent component.
Handle drag-and-drop events
Override callbacks in the DragAndDropTarget
interface to observe when a
drag-and-drop event starts, ends, or enters or exits a component for precise
control of the UI and the app's behavior:
object : DragAndDropTarget { override fun onStarted(event: DragAndDropEvent) { // When the drag event starts } override fun onEntered(event: DragAndDropEvent) { // When the dragged object enters the target surface } override fun onEnded(event: DragAndDropEvent) { // When the drag event stops } override fun onExited(event: DragAndDropEvent) { // When the dragged object exits the target surface } override fun onDrop(event: DragAndDropEvent): Boolean = true }
Additional resources
Codelab: Drag and drop in Compose