The aim of this library is to provide utilities that simplify gesture-based interactions, starting with easily configured drag-and-drop functionality.
This library is compatible with Compose Multiplatform and currently supports the iOS and Android targets.
In Jetpack Compose, gesture-based behavior is added to composables via Compose modifiers. For instance, the Modifier.draggable
modifier can be used to detect when a composable is being dragged in a single orientation. To handle more complex cases of dragging composables, the Modifier.pointerInput
modifier is available.
In both of these cases, detection of user input is handled separately from actually moving the dragged composable on the screen; to move the composable, the detected offset must be passed to Modifier.offset
. On top of this, there is no default concept of drag-and-drop where a receiving composable (a "drop target") can be configured to receive data from the dragged composable (a "drag target"), nor is there any built-in concept of the current state of the drag or drop targets (e.g. being hovered over).
The DragContext
API was created to enable flexible drag-and-drop behavior while encapsulating the complexity of the implementation details so that developers can focus on core app logic instead. Phrased differently, the DragContext
API is meant to be a declarative way to specify drag-and-drop behavior — to specify what you want to be dragged and dropped without needing to specify how.
To use the DragContext
API, first create an instance of DragContext<T>
where T
is the type parameter specifying the type of data that will be dropped into the drop target:
val dragContext = DragContext<Int>()
Next, specify a drag target by wrapping the desired composable in the DragTarget
composable, which is a member function of the dragContext
instance. For convenience, the withDragContext
function is provided so that you can do:
withDragContext(dragContext) {
DragTarget(data = ...) {
// Your composable here
}
}
instead of
dragContext.DragTarget(data = ...) { ... }
The next step is to specify a drop target by wrapping the desired composable in the DropTarget
composable, similar to how DragTarget
was configured:
withDragContext(dragContext) {
DropTarget(onDragTargetAdded = { ... }) {
// Your composable here
}
}
Note: It is important that the same DragContext
instance be used for both the DragTarget
and DropTarget
. This is because all of the internal state of the drag-and-drop items — e.g. the drag targets' positions, the drop targets' callbacks, etc. — are all contained internally in the DragContext
.
Tip: While in this example the dragContext
is locally accessible to both the DragTarget
and DropTarget
calls, this may not be the case in a larger, more complex application. In such cases, the DragContext
instance can be provided to deeply nested areas of the application by creating a CompositionLocal
. For an example of this, see the susumunoda/word-game
repository: where the CompositionLocal
is defined, where it is used to set up DragTarget
s, and where it is used to set up DropTarget
s.
TODO
drag_context_demo_10-10-23.mov
See the source code.
For a more complex example, see susumunoda/word-game.
It is worth noting that the DragContext
API does not currently take advantage of the experimental AnchoredDraggable
API. It is possible that as that API matures, the DragContext
API may also change or possibly no longer be necessary.