### Implement Drag Handle in DraggableItem Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/drag-handle.md Shows the basic setup for a DraggableItem using a drag handle. Ensure the ExperimentalDndApi is opted in. ```kotlin @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") } } ``` -------------------------------- ### Create a Reorderable LazyColumn List Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/reorder.md A complete example demonstrating a reorderable list using LazyColumn and ReorderableItem. Includes custom item rendering and drag shadow handling. ```kotlin @Composable fun ReorderListExample() { val reorderState = rememberReorderState() var items by remember { mutableStateOf( listOf("Item 1", "Item 2", "Item 3", "Item 4", "Item 5") ) } val lazyListState = rememberLazyListState() ReorderContainer( state = reorderState, modifier = Modifier.fillMaxSize().padding(20.dp), ) { LazyColumn( state = lazyListState, verticalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier.fillMaxSize(), ) { items(items, key = { it }) { item -> ReorderableItem( state = reorderState, key = item, data = item, onDrop = {}, onDragEnter = { state -> items = items.toMutableList().apply { val index = indexOf(item) if (index == -1) return@ReorderableItem remove(state.data) add(index, state.data) } }, draggableContent = { ItemCard( text = item, isDragShadow = true, ) }, modifier = Modifier.fillMaxWidth(), ) { ItemCard( text = item, modifier = Modifier .graphicsLayer { alpha = if (isDragging) 0f else 1f } .fillMaxWidth(), ) } } } } } @Composable private fun ItemCard( text: String, isDragShadow: Boolean = false, modifier: Modifier = Modifier, ) { Card( modifier = modifier.height(60.dp), elevation = CardDefaults.cardElevation( defaultElevation = if (isDragShadow) 8.dp else 2.dp, ), ) { Box( contentAlignment = Alignment.CenterStart, modifier = Modifier .fillMaxSize() .padding(horizontal = 16.dp), ) { Text(text = text) } } } ``` -------------------------------- ### LazyVerticalGrid Auto Scroll Example Source: https://context7.com/mohamedrejeb/compose-dnd/llms.txt Shows how to implement auto-scrolling for a LazyVerticalGrid when dragging items. This example uses default auto-scroll settings. ```kotlin @OptIn(ExperimentalDndApi::class) @Composable fun GridAutoScrollExample() { val reorderState = rememberReorderState() val lazyGridState = rememberLazyGridState() var items by remember { mutableStateOf((1..100).toList()) } ReorderContainer(state = reorderState) { LazyVerticalGrid( columns = GridCells.Fixed(3), state = lazyGridState, modifier = Modifier .fillMaxSize() .dragAutoScroll( state = reorderState.dndState, lazyGridState = lazyGridState, ) ) { items(items, key = { it }) { ReorderableItem( state = reorderState, key = item, data = item, onDragEnter = { state -> items = items.toMutableList().apply { val index = indexOf(item) if (index != -1) { remove(state.data) add(index, state.data) } } }, ) { Box( contentAlignment = Alignment.Center, modifier = Modifier .aspectRatio(1f) .padding(4.dp) .graphicsLayer { alpha = if (isDragging) 0f else 1f } .background(MaterialTheme.colorScheme.primaryContainer) ) { Text("$item") } } } } } } ``` -------------------------------- ### Compose Kanban Board with Conditional Drop Source: https://context7.com/mohamedrejeb/compose-dnd/llms.txt A Kanban board example showcasing conditional dropping. It includes columns for 'To Do', 'In Progress' (with a capacity limit), and 'Done' (which only accepts 'In Progress' tasks). ```kotlin @OptIn(ExperimentalDndApi::class) @Composable fun ConditionalDropExample() { val dragAndDropState = rememberDragAndDropState() var todoTasks by remember { mutableStateOf(listOf( KanbanTask("1", "Design UI", TaskStatus.TODO), KanbanTask("2", "Write tests", TaskStatus.TODO) )) } var inProgressTasks by remember { mutableStateOf(listOf()) } var doneTasks by remember { mutableStateOf(listOf()) } val maxInProgress = 3 // WIP limit DragAndDropContainer(state = dragAndDropState) { Row( horizontalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier.fillMaxSize().padding(16.dp) ) { // TODO column - accepts any task KanbanColumn( title = "To Do", tasks = todoTasks, state = dragAndDropState, columnKey = "todo", canDrop = { true }, // Always accepts onDrop = { task -> todoTasks = todoTasks + task.copy(status = TaskStatus.TODO) inProgressTasks = inProgressTasks.filter { it.id != task.id } doneTasks = doneTasks.filter { it.id != task.id } }, modifier = Modifier.weight(1f) ) // In Progress column - limited capacity KanbanColumn( title = "In Progress (${inProgressTasks.size}/$maxInProgress)", tasks = inProgressTasks, state = dragAndDropState, columnKey = "inProgress", canDrop = { inProgressTasks.size < maxInProgress }, // Capacity limit onDrop = { task -> inProgressTasks = inProgressTasks + task.copy(status = TaskStatus.IN_PROGRESS) todoTasks = todoTasks.filter { it.id != task.id } doneTasks = doneTasks.filter { it.id != task.id } }, modifier = Modifier.weight(1f) ) // Done column - only accepts IN_PROGRESS tasks KanbanColumn( title = "Done", tasks = doneTasks, state = dragAndDropState, columnKey = "done", canDrop = { state -> state.data.status == TaskStatus.IN_PROGRESS // Type validation }, onDrop = { task -> doneTasks = doneTasks + task.copy(status = TaskStatus.DONE) inProgressTasks = inProgressTasks.filter { it.id != task.id } }, modifier = Modifier.weight(1f) ) } } } ``` -------------------------------- ### LazyColumn Auto Scroll Example Source: https://context7.com/mohamedrejeb/compose-dnd/llms.txt Demonstrates how to enable auto-scrolling for a LazyColumn when dragging items near its edges. Includes custom configuration for scroll thresholds and speed. ```kotlin @OptIn(ExperimentalDndApi::class) @Composable fun AutoScrollExample() { val reorderState = rememberReorderState() var items by remember { mutableStateOf((1..50).map { "Item $it" }) } val lazyListState = rememberLazyListState() // Custom auto scroll configuration val autoScrollConfig = DragAutoScrollConfig( minScrollThreshold = 48.dp, // Distance from edge to start scrolling maxScrollSpeed = 1500f // Max scroll speed in pixels/second ) ReorderContainer(state = reorderState) { LazyColumn( state = lazyListState, verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier .fillMaxSize() .padding(16.dp) .dragAutoScroll( state = reorderState.dndState, lazyListState = lazyListState, config = autoScrollConfig, ) ) { items(items, key = { it }) { ReorderableItem( state = reorderState, key = item, data = item, onDragEnter = { draggedState -> items = items.toMutableList().apply { val index = indexOf(item) if (index != -1) { remove(draggedState.data) add(index, draggedState.data) } } }, ) { Card( modifier = Modifier .fillMaxWidth() .graphicsLayer { alpha = if (isDragging) 0f else 1f } ) { Text(item, modifier = Modifier.padding(16.dp)) } } } } } } ``` -------------------------------- ### Jetpack Compose Drag and Drop Example Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/overview.md This composable function sets up a drag and drop interface. It requires remembering the drag and drop state and managing the state of whether an item has been dropped. Use this for implementing interactive drag and drop features in your Compose UI. ```kotlin @Composable fun DragAndDropExample() { val dragAndDropState = rememberDragAndDropState() var isDropped by remember { mutableStateOf(false) } DragAndDropContainer( state = dragAndDropState, modifier = Modifier.fillMaxSize(), ) { Column( verticalArrangement = Arrangement.spacedBy(20.dp), modifier = Modifier.fillMaxSize().padding(20.dp), ) { // Source area Box( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxWidth() .weight(1f) .border(1.dp, Color.Gray, RoundedCornerShape(16.dp)), ) { if (!isDropped) { DraggableItem( state = dragAndDropState, key = 1, data = 1, ) { Box( modifier = Modifier .graphicsLayer { alpha = if (isDragging) 0f else 1f } .size(100.dp) .background(Color.Red, RoundedCornerShape(16.dp)), contentAlignment = Alignment.Center, ) { Text("Drag me", color = Color.White) } } } } // Target area val isHovered = dragAndDropState.hoveredDropTargetKey == "target" Box( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxWidth() .weight(1f) .border( width = 2.dp, color = if (isHovered) Color.Blue else Color.Gray, shape = RoundedCornerShape(16.dp), ) .dropTarget( key = "target", state = dragAndDropState, onDrop = { isDropped = true }, ), ) { if (isDropped) { Box( modifier = Modifier .size(100.dp) .background(Color.Red, RoundedCornerShape(16.dp)), contentAlignment = Alignment.Center, ) { Text("Dropped!", color = Color.White) } } else { Text("Drop here", color = Color.Gray) } } } } } ``` -------------------------------- ### Drag And Drop Container Example Source: https://context7.com/mohamedrejeb/compose-dnd/llms.txt A container composable that wraps draggable items and drop targets, handling input and rendering drag shadows. It can be enabled or disabled and contains draggable items and a drop target area. ```kotlin @Composable fun ContainerExample() { val dragAndDropState = rememberDragAndDropState() var isEnabled by remember { mutableStateOf(true) } DragAndDropContainer( state = dragAndDropState, modifier = Modifier .fillMaxSize() .padding(16.dp), enabled = isEnabled, // Toggle to disable all drag and drop ) { Column( verticalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier.fillMaxSize() ) { // Source items Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { listOf("Apple", "Banana", "Cherry").forEach { fruit -> DraggableItem( state = dragAndDropState, key = fruit, data = fruit, ) { Card( modifier = Modifier.graphicsLayer { alpha = if (isDragging) 0f else 1f } ) { Text(fruit, modifier = Modifier.padding(16.dp)) } } } } // Drop target Box( modifier = Modifier .fillMaxWidth() .height(200.dp) .dropTarget( key = "basket", state = dragAndDropState, onDrop = { state -> println("Dropped: ${state.data}") } ) ) { Text("Drop fruits here") } } } } ``` -------------------------------- ### ScrollState Auto Scroll Example Source: https://context7.com/mohamedrejeb/compose-dnd/llms.txt Illustrates enabling auto-scrolling for a standard scrollable container (like Column or Row) using its ScrollState. This is for non-lazy layouts. ```kotlin @OptIn(ExperimentalDndApi::class) @Composable fun ScrollStateAutoScrollExample() { val reorderState = rememberReorderState() val scrollState = rememberScrollState() ReorderContainer(state = reorderState) { Column( modifier = Modifier .fillMaxSize() .verticalScroll(scrollState) .dragAutoScroll( state = reorderState.dndState, scrollState = scrollState, orientation = Orientation.Vertical, ) ) { // Items... } } } ``` -------------------------------- ### ReorderContainer Example Source: https://context7.com/mohamedrejeb/compose-dnd/llms.txt Demonstrates how to use ReorderContainer with a LazyColumn to create a reorderable list. The onDragEnter lambda is used to update the list's order when an item is dragged over another. ```kotlin import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Card import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.unit.dp import com.github.skydoves.compose.reorderable.ReorderContainer import com.github.skydoves.compose.reorderable.ReorderableItem import com.github.skydoves.compose.reorderable.rememberReorderState @Composable fun ReorderContainerExample() { val reorderState = rememberReorderState() var items by remember { mutableStateOf(listOf("First", "Second", "Third", "Fourth")) } ReorderContainer( state = reorderState, modifier = Modifier.fillMaxSize().padding(16.dp), enabled = true, ) { LazyColumn( verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxSize() ) { items(items, key = { it }) { ReorderableItem( state = reorderState, key = item, data = item, onDrop = { /* Optional: handle final drop */ }, onDragEnter = { // Reorder list when dragged item enters this position items = items.toMutableList().apply { val targetIndex = indexOf(item) if (targetIndex != -1) { remove(draggedState.data) add(targetIndex, draggedState.data) } } }, ) { Card( modifier = Modifier .fillMaxWidth() .graphicsLayer { alpha = if (isDragging) 0f else 1f } ) { Text(item, modifier = Modifier.padding(16.dp)) } } } } } } ``` -------------------------------- ### Configure Drop Strategies and Z-Index Source: https://context7.com/mohamedrejeb/compose-dnd/llms.txt Demonstrates the three built-in drop strategies and how to use z-index to define priority for overlapping drop targets. ```kotlin @Composable fun DropStrategyExample() { val dragAndDropState = rememberDragAndDropState() DragAndDropContainer(state = dragAndDropState) { // SurfacePercentage (Default): Highest percentage of target covered DraggableItem( state = dragAndDropState, key = "item-1", data = "Data 1", dropStrategy = DropStrategy.SurfacePercentage, ) { Text("Default Strategy") } // Surface: Largest absolute overlapping area DraggableItem( state = dragAndDropState, key = "item-2", data = "Data 2", dropStrategy = DropStrategy.Surface, ) { Text("Surface Strategy") } // CenterDistance: Closest center-to-center distance DraggableItem( state = dragAndDropState, key = "item-3", data = "Data 3", dropStrategy = DropStrategy.CenterDistance, ) { Text("Center Distance Strategy") } // Drop targets with z-index for priority Box( modifier = Modifier .size(200.dp) .dropTarget( key = "background", state = dragAndDropState, zIndex = 0f, // Lower priority onDrop = { println("Dropped on background") } ) ) { Box( modifier = Modifier .size(100.dp) .dropTarget( key = "foreground", state = dragAndDropState, zIndex = 1f, // Higher priority - wins when overlapping onDrop = { println("Dropped on foreground") } ) ) } } } ``` -------------------------------- ### Implement and Use Custom Drop Strategy Source: https://context7.com/mohamedrejeb/compose-dnd/llms.txt Shows how to create a custom strategy by implementing the DropStrategy interface and applying it to a DraggableItem. ```kotlin object ProximityDropStrategy : DropStrategy { override fun getHoveredDropTarget( draggedItemTopLeft: Offset, draggedItemSize: Size, dropTargets: List>, ): DropTargetState? { val draggedCenter = Offset( draggedItemTopLeft.x + draggedItemSize.width / 2, draggedItemTopLeft.y + draggedItemSize.height / 2 ) return dropTargets.minByOrNull { target -> val targetCenter = Offset( target.topLeft.x + target.size.width / 2, target.topLeft.y + target.size.height / 2 ) (draggedCenter - targetCenter).getDistance() } } } // Usage of custom strategy @Composable fun CustomStrategyUsage() { val state = rememberDragAndDropState() DraggableItem( state = state, key = "custom", data = "Custom Data", dropStrategy = ProximityDropStrategy, ) { Text("Custom Strategy Item") } } ``` -------------------------------- ### Implement Drag and Drop with Categorized Targets in Kotlin Source: https://context7.com/mohamedrejeb/compose-dnd/llms.txt Demonstrates a full drag and drop implementation using DragAndDropContainer, DraggableItem, and custom drop targets. Requires the compose-dnd library and appropriate state management. ```kotlin @Composable fun CompleteDragAndDropExample() { val dragAndDropState = rememberDragAndDropState() var availableFruits by remember { mutableStateOf( listOf( Fruit("apple", "Apple", FruitCategory.POME), Fruit("banana", "Banana", FruitCategory.TROPICAL), Fruit("cherry", "Cherry", FruitCategory.STONE), Fruit("mango", "Mango", FruitCategory.TROPICAL), Fruit("peach", "Peach", FruitCategory.STONE), Fruit("pear", "Pear", FruitCategory.POME) ) ) } var tropicalBasket by remember { mutableStateOf(listOf()) } var stoneBasket by remember { mutableStateOf(listOf()) } var pomeBasket by remember { mutableStateOf(listOf()) } DragAndDropContainer( state = dragAndDropState, modifier = Modifier.fillMaxSize().padding(16.dp) ) { Column( verticalArrangement = Arrangement.spacedBy(24.dp), modifier = Modifier.fillMaxSize() ) { // Source area with draggable fruits Text("Available Fruits", style = MaterialTheme.typography.titleMedium) LazyRow( horizontalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier.fillMaxWidth() ) { items(availableFruits, key = { it.id }) { fruit -> DraggableItem( state = dragAndDropState, key = fruit.id, data = fruit, draggableContent = { FruitChip(fruit = fruit, elevation = 8.dp) } ) { FruitChip( fruit = fruit, modifier = Modifier.graphicsLayer { alpha = if (isDragging) 0f else 1f } ) } } } // Drop target baskets Text("Sort into Baskets", style = MaterialTheme.typography.titleMedium) Row( horizontalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier.fillMaxWidth().weight(1f) ) { FruitBasket( title = "Tropical", fruits = tropicalBasket, targetKey = "tropical", state = dragAndDropState, acceptsCategory = FruitCategory.TROPICAL, onDrop = { fruit -> tropicalBasket = tropicalBasket + fruit availableFruits = availableFruits.filter { it.id != fruit.id } }, modifier = Modifier.weight(1f) ) FruitBasket( title = "Stone Fruits", fruits = stoneBasket, targetKey = "stone", state = dragAndDropState, acceptsCategory = FruitCategory.STONE, onDrop = { fruit -> stoneBasket = stoneBasket + fruit availableFruits = availableFruits.filter { it.id != fruit.id } }, modifier = Modifier.weight(1f) ) FruitBasket( title = "Pome Fruits", fruits = pomeBasket, targetKey = "pome", state = dragAndDropState, acceptsCategory = FruitCategory.POME, onDrop = { fruit -> pomeBasket = pomeBasket + fruit availableFruits = availableFruits.filter { it.id != fruit.id } }, modifier = Modifier.weight(1f) ) } } } } @OptIn(ExperimentalDndApi::class) @Composable fun FruitBasket( title: String, fruits: List, targetKey: String, state: DragAndDropState, acceptsCategory: FruitCategory, onDrop: (Fruit) -> Unit, modifier: Modifier = Modifier ) { val isHovered = state.hoveredDropTargetKey == targetKey val draggedFruit = state.draggedItem?.data val wouldAccept = draggedFruit?.category == acceptsCategory Column( modifier = modifier .fillMaxHeight() .border( width = 2.dp, color = when { isHovered && wouldAccept -> Color.Green isHovered && !wouldAccept -> Color.Red else -> Color.Gray }, ``` -------------------------------- ### Configure Compose DND in Version Catalog Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/installation.md Define the Compose DND version and library in your libs.versions.toml file for version catalog management. ```toml [versions] compose-dnd = "{{ compose_dnd_version }}" [libraries] compose-dnd = { module = "com.mohamedrejeb.dnd:compose-dnd", version.ref = "compose-dnd" } ``` -------------------------------- ### Observing Drag State Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/overview.md Explains how to observe the current drag state, including hovered drop targets and whether an item is being dragged. ```APIDOC ## Observing Drag State ### Description Allows observation of the current drag and drop state, enabling dynamic UI updates based on drag events. ### Observing Hovered Drop Target You can observe which drop target is currently hovered using `DragAndDropState.hoveredDropTargetKey`. ```kotlin val isHovered = dragAndDropState.hoveredDropTargetKey == "target-1" Box( modifier = Modifier .border( width = 2.dp, color = if (isHovered) Color.Blue else Color.Gray, ) .dropTarget( key = "target-1", state = dragAndDropState, onDrop = { /* ... */ }, ) ) { Text("Drop here") } ``` ### Observing Dragging State You can also check if any item is being dragged using `DragAndDropState.draggedItem`. ```kotlin val isDragging = dragAndDropState.draggedItem != null ``` ``` -------------------------------- ### Create a FruitChip Composable Source: https://context7.com/mohamedrejeb/compose-dnd/llms.txt A composable function to display a fruit item as a chip. It uses `Surface` for styling and `Text` for the fruit name. ```kotlin @Composable fun FruitChip(fruit: Fruit, modifier: Modifier = Modifier, elevation: Dp = 2.dp) { Surface( modifier = modifier, shape = RoundedCornerShape(16.dp), tonalElevation = elevation ) { Text( text = fruit.name, modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp) ) } } ``` -------------------------------- ### Implement Custom Drop Strategy Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/drop-strategies.md Create a custom strategy by implementing the DropStrategy interface and passing it to the DraggableItem. ```kotlin object MyCustomStrategy : DropStrategy { override fun getHoveredDropTarget( draggedItemTopLeft: Offset, draggedItemSize: Size, dropTargets: List>, ): DropTargetState? { // Your custom logic here return dropTargets.firstOrNull() } } ``` ```kotlin DraggableItem( state = dragAndDropState, key = "item-1", data = "Hello", dropStrategy = MyCustomStrategy, ) { // ... } ``` -------------------------------- ### Reference Compose DND from Version Catalog in Gradle Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/installation.md Use the version catalog alias to include the Compose DND dependency in your build.gradle.kts file. ```kotlin implementation(libs.compose.dnd) ``` -------------------------------- ### Create DragAndDropState Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/overview.md Initialize the state instance using the required data type parameter. ```kotlin val dragAndDropState = rememberDragAndDropState() ``` -------------------------------- ### Implement DragAndDropContainer Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/overview.md Wrap all draggable items and drop targets within this container to enable pointer tracking and drag shadow rendering. ```kotlin DragAndDropContainer( state = dragAndDropState, modifier = Modifier.fillMaxSize(), enabled = true, ) { // Draggable items and drop targets go here } ``` -------------------------------- ### Implement Reorderable List Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/README.md Use ReorderContainer and ReorderableItem to create lists that support drag-to-reorder functionality. ```kotlin val reorderState = rememberReorderState() ReorderContainer( state = reorderState, ) { LazyColumn { items(items, key = { it }) { item -> ReorderableItem( state = reorderState, key = item, data = item, onDrop = {}, onDragEnter = { state -> items = items.toMutableList().apply { val index = indexOf(item) if (index == -1) return@ReorderableItem remove(state.data) add(index, state.data) } }, ) { Text(item) } } } } ``` -------------------------------- ### Creating ReorderState Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/reorder.md Use `rememberReorderState` to create and remember a `ReorderState` instance. This state manages the reordering logic. ```APIDOC ## Creating ReorderState Use `rememberReorderState` to create and remember a `ReorderState` instance: ```kotlin val reorderState = rememberReorderState() ``` ### Parameters | Parameter | Type | Default | Description | |----------------------|-----------|---------|---------------------------------------------------------------------------------| | `dragAfterLongPress` | `Boolean` | `false` | If `true`, drag starts after a long press. Applied to all items unless overridden. | ``` -------------------------------- ### Configure Built-in Drop Strategies Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/drop-strategies.md Apply built-in strategies to DraggableItem to control how drop targets are selected based on overlap or proximity. ```kotlin DraggableItem( state = dragAndDropState, key = "item-1", data = "Hello", dropStrategy = DropStrategy.SurfacePercentage, ) { // ... } ``` ```kotlin DraggableItem( state = dragAndDropState, key = "item-1", data = "Hello", dropStrategy = DropStrategy.Surface, ) { // ... } ``` ```kotlin DraggableItem( state = dragAndDropState, key = "item-1", data = "Hello", dropStrategy = DropStrategy.CenterDistance, ) { // ... } ``` -------------------------------- ### License Information Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/index.md The library is licensed under the Apache License, Version 2.0. ```text Copyright 2023 Mohamed Rejeb Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ``` -------------------------------- ### Drop Target Configuration Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/overview.md Configures a composable as a drop target with various customization options. ```APIDOC ## Drop Target Configuration ### Description Configures a composable as a drop target, allowing it to receive dragged items. Various parameters control its behavior and appearance during drag and drop operations. ### Method `dropTarget` modifier ### Endpoint N/A (Modifier) ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Parameters - **key** (Any) - Required - Unique key identifying this drop target. - **state** (DragAndDropState) - Required - The drag and drop state. - **zIndex** (Float) - Optional - Z-index for overlapping targets. Higher values take priority. Default: `0f`. - **dropAlignment** (Alignment) - Optional - Alignment of the dropped item within the target for the drop animation. Default: `Alignment.Center`. - **dropOffset** (Offset) - Optional - Additional offset for the drop animation position. Default: `Offset.Zero`. - **dropAnimationEnabled** (Boolean) - Optional - Whether to animate the drop. If `false`, the drop callback fires immediately. Default: `true`. - **canDrop** (Boolean) - Optional - Whether this target accepts drops. When `false`, dragged items cannot hover over or be dropped on this target. Can read `state.draggedItem?.data` to validate dynamically. Default: `true`. - **onDrop** ((DraggedItemState) -> Unit) - Optional - Called when an item is dropped on this target. Default: `{}`. - **onDragEnter** ((DraggedItemState) -> Unit) - Optional - Called when a dragged item enters this target. Default: `{}`. - **onDragExit** ((DraggedItemState) -> Unit) - Optional - Called when a dragged item exits this target. Default: `{}`. ### Request Example N/A (Modifier) ### Response N/A (Modifier) ### DraggedItemState The callbacks receive a `DraggedItemState` with the following properties: - `key: Any` -- The key of the dragged item. - `data: T` -- The data associated with the dragged item. - `dragAmount: Offset` -- The total drag offset from the original position. ``` -------------------------------- ### Create ReorderState Instance Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/reorder.md Use `rememberReorderState` to create and remember a `ReorderState` instance for managing reordering logic. The `dragAfterLongPress` parameter can be set to true to initiate drag after a long press. ```kotlin val reorderState = rememberReorderState() ``` -------------------------------- ### Implement Drag and Drop in Compose Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/index.md Use DragAndDropContainer, DraggableItem, and the dropTarget modifier to enable drag and drop functionality. ```kotlin val dragAndDropState = rememberDragAndDropState() DragAndDropContainer( state = dragAndDropState, ) { DraggableItem( state = dragAndDropState, key = "item-1", data = "Hello", ) { Text("Drag me") } Box( modifier = Modifier .dropTarget( key = "target-1", state = dragAndDropState, onDrop = { state -> println("Dropped: ${state.data}") }, ) ) { Text("Drop here") } } ``` -------------------------------- ### Observe Reorder State Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/reorder.md Accesses the currently dragged item and the key of the drop target currently being hovered. ```kotlin // Currently dragged item val draggedItem = reorderState.draggedItem // Key of the drop target currently being hovered val hoveredKey = reorderState.hoveredDropTargetKey ``` -------------------------------- ### Add Compose DND Dependency to Gradle Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/installation.md Include this dependency in your module's build.gradle.kts file for standard Android or JVM projects. ```kotlin implementation("com.mohamedrejeb.dnd:compose-dnd:{{ compose_dnd_version }}") ``` -------------------------------- ### Add Dependency to build.gradle.kts Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/README.md Include the library dependency in your module's build configuration. ```kotlin implementation("com.mohamedrejeb.dnd:compose-dnd:0.5.0") ``` ```kotlin kotlin { sourceSets { commonMain.dependencies { implementation("com.mohamedrejeb.dnd:compose-dnd:0.5.0") } } } ``` -------------------------------- ### Implement a Drop Target with Callbacks Source: https://context7.com/mohamedrejeb/compose-dnd/llms.txt Use the `dropTarget` modifier to designate a composable as a drop target. Configure callbacks for `onDrop`, `onDragEnter`, and `onDragExit` to handle item interactions. The `key` parameter is essential for identifying the drop target, and `zIndex` determines its drop priority. ```kotlin @Composable fun DropTargetExample() { val dragAndDropState = rememberDragAndDropState() var droppedItems by remember { mutableStateOf(listOf()) } DragAndDropContainer(state = dragAndDropState) { Column(modifier = Modifier.fillMaxSize().padding(16.dp)) { // Draggable items source Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { listOf("Red", "Green", "Blue").forEach { color -> DraggableItem( state = dragAndDropState, key = color, data = color, ) { Box( modifier = Modifier .size(60.dp) .graphicsLayer { alpha = if (isDragging) 0f else 1f } .background( when (color) { "Red" -> Color.Red "Green" -> Color.Green else -> Color.Blue }, RoundedCornerShape(8.dp) ) ) } } } Spacer(Modifier.height(32.dp)) // Drop target with full configuration val isHovered = dragAndDropState.hoveredDropTargetKey == "drop-zone" Box( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxWidth() .height(200.dp) .border( width = 2.dp, color = if (isHovered) Color.Blue else Color.Gray, shape = RoundedCornerShape(16.dp) ) .dropTarget( key = "drop-zone", state = dragAndDropState, zIndex = 0f, // Priority for overlapping targets dropAlignment = Alignment.Center, // Where item animates to dropOffset = Offset.Zero, // Additional offset dropAnimationEnabled = true, // Animate drop onDrop = { state -> droppedItems = droppedItems + state.data println("Dropped: ${state.data}, total drag: ${state.dragAmount}") }, onDragEnter = { state -> println("Entered with: ${state.data}") }, onDragExit = { state -> println("Exited with: ${state.data}") }, ) ) { Column(horizontalAlignment = Alignment.CenterHorizontally) { Text(if (isHovered) "Release to drop!" else "Drop colors here") Text("Items: ${droppedItems.joinToString()}") } } } } } ``` -------------------------------- ### Implement ReorderContainer Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/reorder.md All reorderable items must be placed inside a `ReorderContainer`. This composable manages the state and behavior of reordering within its content. Ensure the `state` parameter is provided. ```kotlin ReorderContainer( state = reorderState, modifier = Modifier.fillMaxSize(), ) { // Reorderable items go here } ``` -------------------------------- ### Apply Drop Strategy to ReorderableItem Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/drop-strategies.md Configure drop strategies for reorderable items to manage drag-and-drop behavior within lists. ```kotlin ReorderableItem( state = reorderState, key = item, data = item, dropStrategy = DropStrategy.CenterDistance, onDragEnter = { state -> // Reorder logic }, ) { // ... } ``` -------------------------------- ### ReorderableItemModifierExample Source: https://context7.com/mohamedrejeb/compose-dnd/llms.txt Demonstrates the usage of the reorderableItem modifier for creating reorderable lists. It requires remembering the DragAndDropState and managing the list of items. ```kotlin import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.unit.dp import com.mohamedrejeb.compose.dnd.DragAndDropContainer import com.mohamedrejeb.compose.dnd.DragAndDropState import com.mohamedrejeb.compose.dnd.reorderableItem import com.mohamedrejeb.compose.dnd.rememberDragAndDropState @Composable fun ReorderableItemModifierExample() { val dndState = rememberDragAndDropState() var items by remember { mutableStateOf(listOf("Alpha", "Beta", "Gamma", "Delta")) } DragAndDropContainer(state = dndState) { LazyColumn( verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxSize().padding(16.dp) ) { items(items, key = { it }) { item -> val isDragging = dndState.isDragging(item) Card( modifier = Modifier .fillMaxWidth() .graphicsLayer { alpha = if (isDragging) 0f else 1f } .reorderableItem( key = item, data = item, state = dndState, onDragEnter = { items = items.toMutableList().apply { val index = indexOf(item) if (index != -1) { remove(draggedState.data) add(index, draggedState.data) } } }, onDrop = { /* Handle drop */ }, onDragExit = { /* Handle exit */ }, draggableContent = { Card(elevation = CardDefaults.cardElevation(8.dp)) { Text(item, modifier = Modifier.padding(16.dp)) } }, ) ) { Text(item, modifier = Modifier.padding(16.dp)) } } } } } ``` -------------------------------- ### Remember Drag And Drop State Source: https://context7.com/mohamedrejeb/compose-dnd/llms.txt Creates and remembers a DragAndDropState instance to manage drag and drop operations. Supports basic usage, drag after long press, and requiring unconsumed first down events. Allows observing drag state like isDragging, hoveredTargetKey, and draggedData. ```kotlin @Composable fun DragAndDropExample() { // Basic usage - drag starts on touch move val dragAndDropState = rememberDragAndDropState() // With long press to initiate drag val longPressDragState = rememberDragAndDropState( dragAfterLongPress = true ) // Require unconsumed first down event val unconsumedState = rememberDragAndDropState( dragAfterLongPress = false, requireFirstDownUnconsumed = true ) // Observing drag state val isDragging = dragAndDropState.draggedItem != null val hoveredTargetKey = dragAndDropState.hoveredDropTargetKey val draggedData = dragAndDropState.draggedItem?.data DragAndDropContainer(state = dragAndDropState) { // Content with draggable items and drop targets } } ``` -------------------------------- ### Customize Auto Scroll Behavior Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/auto-scroll.md Configure scroll thresholds and maximum speeds using the DragAutoScrollConfig object. ```kotlin @OptIn(ExperimentalDndApi::class) val config = DragAutoScrollConfig( minScrollThreshold = 48.dp, maxScrollSpeed = 1500f, ) LazyColumn( state = lazyListState, modifier = Modifier .fillMaxSize() .dragAutoScroll( state = reorderState, lazyListState = lazyListState, config = config, ), ) ``` -------------------------------- ### Implement Drag Handle in ReorderableItem Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/drag-handle.md Demonstrates using a drag handle within a ReorderableItem inside a LazyColumn. Requires opting into the ExperimentalDndApi. ```kotlin @OptIn(ExperimentalDndApi::class) @Composable fun DragHandleExample() { val reorderState = rememberReorderState() 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) } } } } } } ``` -------------------------------- ### Enforce Capacity Limits Source: https://github.com/mohamedrejeb/compose-dnd/blob/main/docs/drag-and-drop/conditional-drop.md Limit the number of items a drop zone can accept by checking the current state against a maximum threshold. ```kotlin @OptIn(ExperimentalDndApi::class) val maxItems = 5 var droppedItems by remember { mutableStateOf(listOf()) } Box( modifier = Modifier .dropTarget( key = "limited-zone", state = dragAndDropState, canDrop = { droppedItems.size < maxItems }, onDrop = { state -> droppedItems = droppedItems + state.data }, ) ) { Text("${droppedItems.size} / $maxItems") } ```