### Complete LazyColumn Example with Haptic Feedback Source: https://github.com/calvin-ll/reorderable/blob/main/README.md A complete example demonstrating reordering in LazyColumn with haptic feedback. Ensure you handle list updates and trigger haptics appropriately. ```kotlin val hapticFeedback = LocalHapticFeedback.current var list by remember { mutableStateOf(List(100) { "Item $it" }) } val lazyListState = rememberLazyListState() val reorderableLazyListState = rememberReorderableLazyListState(lazyListState) { from, to -> list = list.toMutableList().apply { add(to.index, removeAt(from.index)) } hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick) } LazyColumn( modifier = Modifier.fillMaxSize(), state = lazyListState, contentPadding = PaddingValues(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), ) { items(list, key = { it }) { ReorderableItem(reorderableLazyListState, key = it) { isDragging -> val elevation by animateDpAsState(if (isDragging) 4.dp else 0.dp) Surface(shadowElevation = elevation) { Row { Text(it, Modifier.padding(horizontal = 8.dp)) IconButton( modifier = Modifier.draggableHandle( onDragStarted = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureThresholdActivate) }, onDragStopped = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureEnd) }, ), onClick = {}, ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } } } ``` -------------------------------- ### LazyVerticalStaggeredGrid Reordering with Haptic Feedback Source: https://github.com/calvin-ll/reorderable/blob/main/README.md This example demonstrates how to add haptic feedback to the reordering process in a LazyVerticalStaggeredGrid. It includes setup for haptic feedback and updates the list state with reordering logic. ```kotlin val hapticFeedback = LocalHapticFeedback.current var list by remember { mutableStateOf(List(100) { "Item $it" }) } val lazyStaggeredGridState = rememberLazyStaggeredGridState() val reorderableLazyStaggeredGridState = rememberReorderableLazyStaggeredGridState(lazyStaggeredGridState) { list = list.toMutableList().apply { this[to.index] = this[from.index].also { this[from.index] = this[to.index] } } hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick) } LazyVerticalStaggeredGrid( columns = StaggeredGridCells.Adaptive(minSize = 96.dp), modifier = Modifier.fillMaxSize(), state = lazyStaggeredGridState, contentPadding = PaddingValues(8.dp), verticalItemSpacing = 8.dp, horizontalArrangement = Arrangement.spacedBy(8.dp), ) { items(list, key = { it }) { ReorderableItem(reorderableLazyStaggeredGridState, key = it) { val elevation by animateDpAsState(if (isDragging) 4.dp else 0.dp) Surface(shadowElevation = elevation) { Row { Text(it, Modifier.padding(horizontal = 8.dp)) IconButton( modifier = Modifier.draggableHandle( onDragStarted = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureThresholdActivate) }, onDragStopped = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureEnd) }, ), onClick = {}, ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } } } ``` -------------------------------- ### LazyHorizontalGrid with Haptic Feedback and Customization Source: https://github.com/calvin-ll/reorderable/blob/main/README.md This example demonstrates a complete setup for a reorderable LazyHorizontalGrid, including haptic feedback on drag events and custom item appearance based on dragging state. It also shows how to update the underlying list and apply various grid configurations. ```kotlin val hapticFeedback = LocalHapticFeedback.current var list by remember { mutableStateOf(List(100) { "Item $it" }) } val lazyGridState = rememberLazyGridState() val reorderableLazyGridState = rememberReorderableLazyGridState(lazyGridState) { from, to -> list = list.toMutableList().apply { this[to.index] = this[from.index].also { this[from.index] = this[to.index] } } hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick) } LazyHorizontalGrid( rows = GridCells.Adaptive(minSize = 96.dp), modifier = Modifier.fillMaxSize(), state = lazyGridState, contentPadding = PaddingValues(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), ) { items(list, key = { it }) { ReorderableItem(reorderableLazyGridState, key = it) { isDragging -> val elevation by animateDpAsState(if (isDragging) 4.dp else 0.dp) Surface(shadowElevation = elevation) { Row { Text(it, Modifier.padding(horizontal = 8.dp)) IconButton( modifier = Modifier.draggableHandle( onDragStarted = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureThresholdActivate) }, onDragStopped = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureEnd) }, ), onClick = {}, ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } } } ``` -------------------------------- ### Basic LazyRow Reorderable Setup Source: https://github.com/calvin-ll/reorderable/blob/main/README.md Demonstrates the fundamental structure for making a LazyRow reorderable. Ensure you provide a unique key for each item. ```kotlin val lazyListState = rememberLazyListState() val reorderableLazyListState = rememberReorderableLazyListState(lazyListState) { from, to -> // Update the list } LazyRow(state = lazyListState) { items(list, key = { /* item key */ }) { ReorderableItem(reorderableLazyListState, key = /* item key */) { isDragging -> // Item content IconButton( modifier = Modifier.draggableHandle(), /* ... */ ) } } } ``` -------------------------------- ### ReorderableColumn with Haptic Feedback Source: https://github.com/calvin-ll/reorderable/blob/main/README.md A complete example of ReorderableColumn demonstrating haptic feedback on drag start, end, and during movement. It also includes list updates and visual feedback with shadow elevation. ```kotlin val hapticFeedback = LocalHapticFeedback.current var list by remember { mutableStateOf(List(4) { "Item $it" }) } ReorderableColumn( modifier = Modifier .fillMaxSize() .padding(8.dp), list = list, onSettle = { list = list.toMutableList().apply { add(toIndex, removeAt(fromIndex)) } }, onMove = { ViewCompat.performHapticFeedback( view, HapticFeedbackConstantsCompat.SEGMENT_FREQUENT_TICK ) }, verticalArrangement = Arrangement.spacedBy(8.dp), ) { key(item) { ReorderableItem { val elevation by animateDpAsState(if (isDragging) 4.dp else 0.dp) Surface(shadowElevation = elevation) { Row { Text(item, Modifier.padding(horizontal = 8.dp)) IconButton( modifier = Modifier.draggableHandle( onDragStarted = { ViewCompat.performHapticFeedback( view, HapticFeedbackConstantsCompat.GESTURE_START ) }, onDragStopped = { ViewCompat.performHapticFeedback( view, HapticFeedbackConstantsCompat.GESTURE_END ) }, ), onClick = {}, ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } } } ``` -------------------------------- ### Basic ReorderableColumn Usage Source: https://github.com/calvin-ll/reorderable/blob/main/README.md A simple example of how to use ReorderableColumn with a list and an onSettle callback to update the list. ```kotlin ReorderableColumn( list = list, onSettle = { // Update the list }, ) { key(item.id) { ReorderableItem { // Item content IconButton(modifier = Modifier.draggableHandle(), /* ... */) } } } ``` -------------------------------- ### Basic LazyVerticalStaggeredGrid Reordering Setup Source: https://github.com/calvin-ll/reorderable/blob/main/README.md This snippet shows the fundamental structure for enabling reordering in a LazyVerticalStaggeredGrid. It initializes the necessary states and wraps list items with ReorderableItem. ```kotlin val lazyStaggeredGridState = rememberLazyStaggeredGridState() val reorderableLazyStaggeredGridState = rememberReorderableLazyStaggeredGridState(lazyStaggeredGridState) { // Update the list } LazyVerticalStaggeredGrid(state = lazyStaggeredGridState) { items(list, key = { /* item key */ }) { ReorderableItem(reorderableLazyStaggeredGridState, key = /* item key */) { // Item content IconButton( modifier = Modifier.draggableHandle(), /* ... */ ) } } } ``` -------------------------------- ### LazyRow Reorderable with Haptic Feedback Source: https://github.com/calvin-ll/reorderable/blob/main/README.md An example of a complete LazyRow implementation that includes haptic feedback for drag and drop actions. The list is updated in place, and haptic feedback is triggered on drag start, stop, and item move. ```kotlin val hapticFeedback = LocalHapticFeedback.current var list by remember { mutableStateOf(List(100) { "Item $it" }) } val lazyListState = rememberLazyListState() val reorderableLazyListState = rememberReorderableLazyListState(lazyListState) { from, to -> list = list.toMutableList().apply { add(to.index, removeAt(from.index)) } hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick) } LazyRow( modifier = Modifier.fillMaxSize(), state = lazyListState, contentPadding = PaddingValues(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), ) { items(list, key = { it }) { ReorderableItem(reorderableLazyListState, key = it) { isDragging -> val elevation by animateDpAsState(if (isDragging) 4.dp else 0.dp) Surface(shadowElevation = elevation) { Column { Text(it, Modifier.padding(vertical = 8.dp)) IconButton( modifier = Modifier.draggableHandle( onDragStarted = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureThresholdActivate) }, onDragStopped = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureEnd) }, ), onClick = {}, ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } } } ``` -------------------------------- ### Basic LazyColumn Reorderable Setup Source: https://github.com/calvin-ll/reorderable/blob/main/README.md This is the basic structure for using the reorderable library with LazyColumn. Remember to update your list within the state's lambda. ```kotlin val lazyListState = rememberLazyListState() val reorderableLazyListState = rememberReorderableLazyListState(lazyListState) { from, to -> // Update the list } LazyColumn(state = lazyListState) { items(list, key = { /* item key */ }) { ReorderableItem(reorderableLazyListState, key = /* item key */) { isDragging -> // Item content IconButton( modifier = Modifier.draggableHandle(), /* ... */ ) } } } ``` -------------------------------- ### Basic LazyHorizontalGrid Reordering Setup Source: https://github.com/calvin-ll/reorderable/blob/main/README.md This snippet shows the fundamental structure for using the reorderable library with LazyHorizontalGrid. It includes remembering the grid state and the reorderable state, and defining the `items` with `ReorderableItem` and `draggableHandle`. ```kotlin val lazyGridState = rememberLazyGridState() val reorderableLazyGridState = rememberReorderableLazyGridState(lazyGridState) { from, to -> // Update the list } LazyHorizontalGrid(state = lazyGridState) { items(list, key = { /* item key */ }) { ReorderableItem(reorderableLazyGridState, key = /* item key */) { isDragging -> // Item content IconButton( modifier = Modifier.draggableHandle(), /* ... */ ) } } } ``` -------------------------------- ### Basic LazyVerticalGrid Reordering Setup Source: https://github.com/calvin-ll/reorderable/blob/main/README.md This snippet shows the fundamental structure for using the reorderable library with LazyVerticalGrid. It includes remembering the grid state and the reorderable state, and defining the `onMove` callback to update the list. ```kotlin val lazyGridState = rememberLazyGridState() val reorderableLazyGridState = rememberReorderableLazyGridState(lazyGridState) { from, to -> // Update the list } LazyVerticalGrid(state = lazyGridState) { items(list, key = { /* item key */ }) { ReorderableItem(reorderableLazyGridState, key = /* item key */) { isDragging -> // Item content IconButton( modifier = Modifier.draggableHandle(), /* ... */ ) } } } ``` -------------------------------- ### LazyVerticalGrid with Haptic Feedback and Styling Source: https://github.com/calvin-ll/reorderable/blob/main/README.md A complete example of a reorderable LazyVerticalGrid that includes haptic feedback on drag events and custom item styling with elevation based on dragging state. Ensure `LocalHapticFeedback` is available in your composition. ```kotlin val hapticFeedback = LocalHapticFeedback.current var list by remember { mutableStateOf(List(100) { "Item $it" }) } val lazyGridState = rememberLazyGridState() val reorderableLazyGridState = rememberReorderableLazyGridState(lazyGridState) { from, to -> list = list.toMutableList().apply { this[to.index] = this[from.index].also { this[from.index] = this[to.index] } } hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick) } LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 96.dp), modifier = Modifier.fillMaxSize(), state = lazyGridState, contentPadding = PaddingValues(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), ) { items(list, key = { it }) { ReorderableItem(reorderableLazyGridState, key = it) { isDragging -> val elevation by animateDpAsState(if (isDragging) 4.dp else 0.dp) Surface(shadowElevation = elevation) { Row { Text(it, Modifier.padding(horizontal = 8.dp)) IconButton( modifier = Modifier.draggableHandle( onDragStarted = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureThresholdActivate) }, onDragStopped = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureEnd) }, ), onClick = {}, ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } } } ``` -------------------------------- ### Basic Reorderable Row Structure Source: https://github.com/calvin-ll/reorderable/blob/main/README.md A foundational example for implementing reorderable functionality within a Row layout. It outlines the basic structure for managing the list and handling item reordering. ```kotlin ReorderableRow( list = list, onSettle = { fromIndex, toIndex -> // Update the list }, ) { index, item, isDragging -> key(item.id) { ReorderableItem { // Item content IconButton(modifier = Modifier.draggableHandle(), /* ... */) } } } ``` -------------------------------- ### Reorderable LazyHorizontalStaggeredGrid with Haptic Feedback Source: https://github.com/calvin-ll/reorderable/blob/main/README.md This example demonstrates a complete implementation with haptic feedback for drag and drop actions. It includes state management for the list, grid, and reorderable state, along with visual feedback for dragging items. ```kotlin val hapticFeedback = LocalHapticFeedback.current var list by remember { mutableStateOf(List(100) { "Item $it" }) } val lazyStaggeredGridState = rememberLazyStaggeredGridState() val reorderableLazyStaggeredGridState = rememberReorderableLazyStaggeredGridState(lazyStaggeredGridState) { list = list.toMutableList().apply { this[to.index] = this[from.index].also { this[from.index] = this[to.index] } } hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick) } LazyHorizontalStaggeredGrid( rows = StaggeredGridCells.Adaptive(minSize = 96.dp), modifier = Modifier.fillMaxSize(), state = lazyStaggeredGridState, contentPadding = PaddingValues(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), horizontalItemSpacing = 8.dp, ) { items(list, key = { it }) { ReorderableItem(reorderableLazyStaggeredGridState, key = it) { isDragging -> val elevation by animateDpAsState(if (isDragging) 4.dp else 0.dp) Surface(shadowElevation = elevation) { Row { Text(it, Modifier.padding(horizontal = 8.dp)) IconButton( modifier = Modifier.draggableHandle( onDragStarted = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureThresholdActivate) }, onDragStopped = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureEnd) }, ), onClick = {}, ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } } } ``` -------------------------------- ### Basic Reorderable LazyHorizontalStaggeredGrid Setup Source: https://github.com/calvin-ll/reorderable/blob/main/README.md This snippet shows the fundamental structure for making a LazyHorizontalStaggeredGrid reorderable. It requires remembering the grid state and the reorderable state, and updating the list within the onMove lambda. ```kotlin val lazyStaggeredGridState = rememberLazyStaggeredGridState() val reorderableLazyStaggeredGridState = rememberReorderableLazyStaggeredGridState(lazyStaggeredGridState) { // Update the list } LazyHorizontalStaggeredGrid(state = lazyStaggeredGridState) { items(list, key = { /* item key */ }) { ReorderableItem(reorderableLazyStaggeredGridState, key = /* item key */) { // Item content IconButton( modifier = Modifier.draggableHandle(), /* ... */ ) } } } ``` -------------------------------- ### Handling Section Headers with Reordering Source: https://github.com/calvin-ll/reorderable/blob/main/README.md This example shows how to adjust item indices when using section headers or footers in a LazyVerticalStaggeredGrid with reordering. It modifies the `onMove` lambda to account for header/footer offsets. ```kotlin var list by remember { mutableStateOf(List(100) { "Item $it" }) } val lazyStaggeredGridState = rememberLazyStaggeredGridState() val reorderableLazyStaggeredGridState = rememberReorderableLazyStaggeredGridState(lazyStaggeredGridState) { list = list.toMutableList().apply { add(to.index - 1, removeAt(from.index - 1)) } } LazyVerticalStaggeredGrid( state = lazyStaggeredGridState, // ... ) { item { Text("Header") } items(list, key = { item -> item.id }) { ReorderableItem(reorderableLazyStaggeredGridState, item.id) { // ... } } } ``` -------------------------------- ### Communicate List Updates via Channel Source: https://github.com/calvin-ll/reorderable/blob/main/README.md If list updates cannot be kept within onMove, use a channel to communicate between onMove and the list update composition. This example shows how to use a Channel to signal list updates. ```kotlin val listUpdatedChannel = remember { Channel() } val reorderableLazyXXXXState = rememberReorderableLazyXXXXState(listState) { from, to -> // clear the channel listUpdatedChannel.tryReceive() // update the list // wait for the list to be updated listUpdatedChannel.receive() } LaunchedEffect(list) { // notify the list is updated listUpdatedChannel.trySend(Unit) } ``` -------------------------------- ### Handling Section Headers with Reordering Source: https://github.com/calvin-ll/reorderable/blob/main/README.md This example illustrates how to adjust item indices when using section headers or footers in a reorderable LazyHorizontalGrid. It shows how to modify the `onMove` lambda to account for the presence of non-item elements. ```kotlin var list by remember { mutableStateOf(List(100) { "Item $it" }) } val lazyGridState = rememberLazyGridState() val reorderableLazyGridState = rememberReorderableLazyGridState(lazyGridState) { from, to -> list = list.toMutableList().apply { add(to.index - 1, removeAt(from.index - 1)) } } LazyHorizontalGrid( state = lazyGridState, // ... ) { item { Text("Header") } items(list, key = { item -> item.id }) { ReorderableItem(reorderableLazyGridState, item.id) { // ... } } } ``` -------------------------------- ### Reorderable Card with Draggable Handle Source: https://github.com/calvin-ll/reorderable/blob/main/README.md Use this snippet to create a reorderable list of Material 3 Cards. Ensure `MutableInteractionSource` is passed to both the `Card` and `draggableHandle` for proper event handling. Haptic feedback is triggered on drag start and end. ```kotlin val hapticFeedback = LocalHapticFeedback.current var list by remember { mutableStateOf(List(4) { "Item $it" }) } ReorderableRow( modifier = Modifier .fillMaxSize() .padding(8.dp), list = list, onSettle = { fromIndex, toIndex -> list = list.toMutableList().apply { add(toIndex, removeAt(fromIndex)) } }, onMove = { ViewCompat.performHapticFeedback( view, HapticFeedbackConstantsCompat.SEGMENT_FREQUENT_TICK ) }, verticalArrangement = Arrangement.spacedBy(8.dp), ) { _, item, _ -> key(item) { ReorderableItem { val interactionSource = remember { MutableInteractionSource() } Card( onClick = {}, interactionSource = interactionSource, ) { Column { Text(item, Modifier.padding(vertical = 8.dp)) IconButton( modifier = Modifier.draggableHandle( onDragStarted = { ViewCompat.performHapticFeedback( view, HapticFeedbackConstantsCompat.GESTURE_START ) }, onDragStopped = { ViewCompat.performHapticFeedback( view, HapticFeedbackConstantsCompat.GESTURE_END ) }, interactionSource = interactionSource, ), onClick = {}, ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } } } ``` -------------------------------- ### Card Integration with Shared Interaction Source Source: https://context7.com/calvin-ll/reorderable/llms.txt Integrate reorderable items with Material3 Cards by sharing a MutableInteractionSource for coordinated visual feedback between the card and drag handle. Haptic feedback is provided on drag start and stop. ```kotlin import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.gestures.draggableHandle import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.DragHandle import androidx.compose.material3.Card import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.unit.dp import org.burnoutcrew.reorderable.* import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Arrangement ``` ```kotlin @Composable fun CardIntegrationExample() { var list by remember { mutableStateOf(List(20) { "Item $it" }) } val lazyListState = rememberLazyListState() val hapticFeedback = LocalHapticFeedback.current val reorderableLazyListState = rememberReorderableLazyListState(lazyListState) { from, to -> list = list.toMutableList().apply { add(to.index, removeAt(from.index)) } hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick) } LazyColumn( state = lazyListState, contentPadding = PaddingValues(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), ) { items(list, key = { it }) { item -> ReorderableItem(reorderableLazyListState, key = item) { isDragging -> // Shared interaction source for coordinated feedback val interactionSource = remember { MutableInteractionSource() } Card( onClick = { println("Card clicked: $item") }, interactionSource = interactionSource, ) { Row( modifier = Modifier.fillMaxWidth().padding(8.dp), verticalAlignment = Alignment.CenterVertically ) { Text(item, modifier = Modifier.weight(1f).padding(8.dp)) IconButton( modifier = Modifier.draggableHandle( interactionSource = interactionSource, onDragStarted = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureThresholdActivate) }, onDragStopped = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureEnd) }, ), onClick = {}, // No-op, handled by Card ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } } } } ``` -------------------------------- ### Handling Section Headers with Reorderable Lists Source: https://github.com/calvin-ll/reorderable/blob/main/README.md This example shows how to adjust item indices when dealing with section headers or footers in a reorderable LazyHorizontalStaggeredGrid. The `onMove` lambda needs to account for the offset introduced by non-item elements. ```kotlin var list by remember { mutableStateOf(List(100) { "Item $it" }) } val lazyStaggeredGridState = rememberLazyStaggeredGridState() val reorderableLazyStaggeredGridState = rememberReorderableLazyStaggeredGridState(lazyStaggeredGridState) { list = list.toMutableList().apply { add(to.index - 1, removeAt(from.index - 1)) } } LazyHorizontalStaggeredGrid( state = lazyStaggeredGridState, // ... ) { item { Text("Header") } items(list, key = { item -> item.id }) { ReorderableItem(reorderableLazyStaggeredGridState, item.id) { // ... } } } ``` -------------------------------- ### ReorderableItem Composable Example Source: https://context7.com/calvin-ll/reorderable/llms.txt Use this composable inside a lazy collection's item scope to make items reorderable. It requires a unique key matching the item's key in the collection. Set 'enabled' to false to disable reordering for a specific item. ```kotlin import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.gestures.animateScrollBy import androidx.compose.foundation.gestures.draggableHandle import androidx.compose.foundation.gestures.longPressDraggableHandle import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.DragHandle import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.lazy.items import androidx.compose.material3.IconButton import androidx.compose.ui.tooling.preview.Preview @Composable fun LazyItemScope.ReorderableItemExample( state: ReorderableLazyListState, item: String ) { ReorderableItem( state = state, key = item, modifier = Modifier.fillMaxWidth(), enabled = true, // Set to false to make item non-reorderable animateItemModifier = Modifier.animateItem(), // Custom animation modifier ) { isDragging -> val elevation by animateDpAsState(if (isDragging) 4.dp else 0.dp) Card( modifier = Modifier.fillMaxWidth(), elevation = CardDefaults.cardElevation(defaultElevation = elevation) ) { Row( modifier = Modifier.padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { Text(item, modifier = Modifier.weight(1f)) // The draggableHandle modifier makes this the drag target Icon( imageVector = Icons.Rounded.DragHandle, contentDescription = "Drag to reorder", modifier = Modifier.draggableHandle() ) } } } } ``` -------------------------------- ### Reorderable LazyVerticalGrid with Material 3 Card Source: https://github.com/calvin-ll/reorderable/blob/main/README.md Use this snippet to create a reorderable grid of Material 3 Cards. Ensure you have `MutableInteractionSource` for both the `Card` and the `draggableHandle` to link drag events. Haptic feedback is provided on drag start, threshold activation, and drag end. ```kotlin val hapticFeedback = LocalHapticFeedback.current var list by remember { mutableStateOf(List(100) { "Item $it" }) } val lazyGridState = rememberLazyGridState() val reorderableLazyGridState = rememberReorderableLazyGridState(lazyGridState) { from, to -> list = list.toMutableList().apply { this[to.index] = this[from.index].also { this[from.index] = this[to.index] } } hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick) } LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 96.dp), modifier = Modifier.fillMaxSize(), state = lazyGridState, contentPadding = PaddingValues(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), ) { items(list, key = { it }) { item -> ReorderableItem(reorderableLazyGridState, key = item) { val interactionSource = remember { MutableInteractionSource() } Card( onClick = {}, interactionSource = interactionSource, ) { Row { Text(item, Modifier.padding(horizontal = 8.dp)) IconButton( modifier = Modifier.draggableHandle( onDragStarted = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureThresholdActivate) }, onDragStopped = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureEnd) }, interactionSource = interactionSource, ), onClick = {}, ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } } } ``` -------------------------------- ### Reorderable LazyVerticalStaggeredGrid with Material 3 Card Source: https://github.com/calvin-ll/reorderable/blob/main/README.md Use this snippet to create a reorderable staggered grid where each item is a Material 3 Card. Ensure you have `MutableInteractionSource` for both the Card and the draggable handle to enable drag event propagation. Haptic feedback is provided on drag start, stop, and during reordering. ```kotlin val hapticFeedback = LocalHapticFeedback.current var list by remember { mutableStateOf(List(100) { "Item $it" }) } val lazyStaggeredGridState = rememberLazyStaggeredGridState() val reorderableLazyStaggeredGridState = rememberReorderableLazyStaggeredGridState(lazyStaggeredGridState) { from, to -> list = list.toMutableList().apply { this[to.index] = this[from.index].also { this[from.index] = this[to.index] } } hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick) } LazyVerticalStaggeredGrid( columns = StaggeredGridCells.Adaptive(minSize = 96.dp), modifier = Modifier.fillMaxSize(), state = lazyStaggeredGridState, contentPadding = PaddingValues(8.dp), verticalItemSpacing = 8.dp, horizontalArrangement = Arrangement.spacedBy(8.dp), ) { items(list, key = { it }) { item -> ReorderableItem(reorderableLazyStaggeredGridState, key = item) { val interactionSource = remember { MutableInteractionSource() } Card( onClick = {}, interactionSource = interactionSource, ) { Row { Text(item, Modifier.padding(horizontal = 8.dp)) IconButton( modifier = Modifier.draggableHandle( onDragStarted = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureThresholdActivate) }, onDragStopped = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureEnd) }, interactionSource = interactionSource, ), onClick = {}, ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } } } ``` -------------------------------- ### Reorderable LazyHorizontalStaggeredGrid with Card Source: https://github.com/calvin-ll/reorderable/blob/main/README.md Use this snippet to create a reorderable grid of Material 3 Cards. It requires setting up `rememberReorderableLazyStaggeredGridState` and passing a `MutableInteractionSource` to both the `Card` and the `draggableHandle` for drag event handling. Haptic feedback is provided on drag start, stop, and reorder. ```kotlin val hapticFeedback = LocalHapticFeedback.current var list by remember { mutableStateOf(List(100) { "Item $it" }) } val lazyStaggeredGridState = rememberLazyStaggeredGridState() val reorderableLazyStaggeredGridState = rememberReorderableLazyStaggeredGridState(lazyStaggeredGridState) { from, to -> list = list.toMutableList().apply { this[to.index] = this[from.index].also { this[from.index] = this[to.index] } } hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick) } LazyHorizontalStaggeredGrid( rows = StaggeredGridCells.Adaptive(minSize = 96.dp), modifier = Modifier.fillMaxSize(), state = lazyStaggeredGridState, contentPadding = PaddingValues(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), horizontalItemSpacing = 8.dp, ) { items(list, key = { it }) { item -> ReorderableItem(reorderableLazyStaggeredGridState, key = item) { val interactionSource = remember { MutableInteractionSource() } Card( onClick = {}, interactionSource = interactionSource, ) { Row { Text(item, Modifier.padding(horizontal = 8.dp)) IconButton( modifier = Modifier.draggableHandle( onDragStarted = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureThresholdActivate) }, onDragStopped = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureEnd) }, interactionSource = interactionSource, ), onClick = {}, ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } } } ``` -------------------------------- ### Pass Modifier.draggableHandle to Child Composable Source: https://github.com/calvin-ll/reorderable/blob/main/README.md When using `Modifier.draggableHandle` or `Modifier.longPressDraggableHandle`, ensure they are applied within the `ReorderableCollectionItemScope`. This example shows how to pass the scope to a child composable. ```kotlin @Composable fun List() { // ... LazyColumn(state = lazyListState) { items(list, key = { /* item key */ }) { ReorderableItem(reorderableLazyListState, key = /* item key */) { isDragging -> // Item content DragHandle(this) } } } } @Composable fun DragHandle(scope: ReorderableCollectionItemScope) { IconButton( modifier = with(scope) { Modifier.draggableHandle() }, /* ... */ ) } ``` -------------------------------- ### Passing DraggableHandle to Child Composable Source: https://github.com/calvin-ll/reorderable/blob/main/README.md This example demonstrates how to pass the `ReorderableListItemScope` to a child composable to use `Modifier.draggableHandle`. This is necessary because `Modifier.draggableHandle` can only be used within the `ReorderableListItemScope`. ```kotlin @Composable fun List() { // ... ReorderableRow( list = list, onSettle = { fromIndex, toIndex -> // Update the list }, ) { index, item, isDragging -> key(item.id) { ReorderableItem { // Item content DragHandle(this) } } } } @Composable fun DragHandle(scope: ReorderableListItemScope) { IconButton(modifier = with(scope) { Modifier.draggableHandle() }, /* ... */) } ``` -------------------------------- ### Add Reorderable Dependency using Version Catalog Source: https://context7.com/calvin-ll/reorderable/llms.txt Add the Reorderable library dependency to your project's `libs.versions.toml` file. ```toml # libs.versions.toml [versions] reorderable = "3.1.0" [libraries] reorderable = { module = "sh.calvin.reorderable:reorderable", version.ref = "reorderable" } ``` -------------------------------- ### Reorderable Item for Lazy Grids Source: https://github.com/calvin-ll/reorderable/blob/main/README.md Wraps individual items in a Lazy Grid to make them reorderable. Requires a unique key for each item and a draggable handle. ```kotlin @Composable fun ReorderableItem(state: ReorderableLazyGridState, key: Any, modifier: Modifier = Modifier, content: @Composable (isDragging: Boolean) -> Unit) { val dragging = state.isDragging(key) val dragModifier = if (dragging) { Modifier.animateItemPlacement() } else { Modifier } content(dragging) .then(dragModifier) .then(state.draggableModifer(key = key)) } ``` -------------------------------- ### Reorderable Item for Lazy Staggered Grids Source: https://github.com/calvin-ll/reorderable/blob/main/README.md Wraps individual items in a Lazy Staggered Grid to make them reorderable. Requires a unique key for each item and a draggable handle. ```kotlin @Composable fun ReorderableItem(state: ReorderableLazyStaggeredGridState, key: Any, modifier: Modifier = Modifier, content: @Composable (isDragging: Boolean) -> Unit) { val dragging = state.isDragging(key) val dragModifier = if (dragging) { Modifier.animateItemPlacement() } else { Modifier } content(dragging) .then(dragModifier) .then(state.draggableModifer(key = key)) } ``` -------------------------------- ### Add Reorderable Dependency using Gradle Source: https://context7.com/calvin-ll/reorderable/llms.txt Include the Reorderable library in your `build.gradle.kts` file. ```kotlin // build.gradle.kts dependencies { implementation(libs.reorderable) // or directly: // implementation("sh.calvin.reorderable:reorderable:3.1.0") } ``` -------------------------------- ### Implement Reorderable Dependency in Kotlin Source: https://github.com/calvin-ll/reorderable/blob/main/README.md After configuring your `libs.versions.toml`, include the reorderable library in your project's dependencies using the `implementation` keyword in your `build.gradle` or `build.gradle.kts` file. ```kotlin dependencies { // ... implementation(libs.reorderable) } ``` -------------------------------- ### Reorderable Row with Haptic Feedback Source: https://github.com/calvin-ll/reorderable/blob/main/README.md This snippet shows a complete implementation of a reorderable row with haptic feedback. It uses `LocalHapticFeedback` and `ViewCompat.performHapticFeedback` to provide tactile feedback during drag operations. Ensure haptic feedback is enabled on the device. ```kotlin val hapticFeedback = LocalHapticFeedback.current var list by remember { mutableStateOf(List(4) { "Item $it" }) } ReorderableRow( modifier = Modifier .fillMaxSize() .padding(8.dp), list = list, onSettle = { fromIndex, toIndex -> list = list.toMutableList().apply { add(toIndex, removeAt(fromIndex)) } }, onMove = { ViewCompat.performHapticFeedback( view, HapticFeedbackConstantsCompat.SEGMENT_TICK ) }, verticalArrangement = Arrangement.spacedBy(8.dp), ) { _, item, isDragging -> key(item) { ReorderableItem { val elevation by animateDpAsState(if (isDragging) 4.dp else 0.dp) Surface(shadowElevation = elevation) { Column { Text(item, Modifier.padding(vertical = 8.dp)) IconButton( modifier = Modifier.draggableHandle( onDragStarted = { ViewCompat.performHapticFeedback( view, HapticFeedbackConstantsCompat.GESTURE_START ) }, onDragStopped = { ViewCompat.performHapticFeedback( view, HapticFeedbackConstantsCompat.GESTURE_END ) }, ), onClick = {}, ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } } } ``` -------------------------------- ### Configure Scroll Trigger Padding for LazyVerticalGrid Source: https://github.com/calvin-ll/reorderable/blob/main/README.md Use `scrollThresholdPadding` with `WindowInsets.systemBars.asPaddingValues()` to adjust the scroll trigger area when the grid is partially covered by system bars. ```kotlin val reorderableLazyGridState = rememberReorderableLazyGridState( lazyGridState = lazyGridState, scrollThresholdPadding = WindowInsets.systemBars.asPaddingValues(), ) { from, to -> ... } ``` -------------------------------- ### Reorderable LazyRow with Material 3 Card and Drag Handle Source: https://github.com/calvin-ll/reorderable/blob/main/README.md Integrates a reorderable `LazyRow` with Material 3 `Card` components. A `MutableInteractionSource` is used to link drag events from `Modifier.draggableHandle` to the `Card` for visual feedback. Haptic feedback is provided on drag start, stop, and item reordering. ```kotlin val hapticFeedback = LocalHapticFeedback.current var list by remember { mutableStateOf(List(100) { "Item $it" }) } val lazyListState = rememberLazyListState() val reorderableLazyListState = rememberReorderableLazyListState(lazyListState) { from, to -> list = list.toMutableList().apply { add(to.index, removeAt(from.index)) } hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick) } LazyRow( modifier = Modifier.fillMaxSize(), state = lazyListState, contentPadding = PaddingValues(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), ) { items(list, key = { it }) { ReorderableItem(reorderableLazyListState, key = item) { val interactionSource = remember { MutableInteractionSource() } Card( onClick = {}, interactionSource = interactionSource, ) { Column { Text(item, Modifier.padding(vertical = 8.dp)) IconButton( modifier = Modifier.draggableHandle( onDragStarted = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureThresholdActivate) }, onDragStopped = { hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureEnd) }, interactionSource = interactionSource, ), onClick = {}, ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } } } ``` -------------------------------- ### Add Scroll Trigger Padding to ReorderableLazyStaggeredGridState Source: https://github.com/calvin-ll/reorderable/blob/main/README.md Use `scrollThresholdPadding` with `WindowInsets.systemBars.asPaddingValues()` when initializing `rememberReorderableLazyStaggeredGridState` to prevent the scroll trigger from being hidden under system bars. ```kotlin val reorderableLazyStaggeredGridState = rememberReorderableLazyStaggeredGridState( lazyStaggeredGridState = lazyStaggeredGridState, scrollThresholdPadding = WindowInsets.systemBars.asPaddingValues(), ) { ... } ``` -------------------------------- ### Configure Scroll Threshold Padding for System Bars Source: https://context7.com/calvin-ll/reorderable/llms.txt Adjust auto-scrolling behavior in reorderable lists under system bars using `scrollThresholdPadding`. This ensures drag operations correctly trigger scrolling near navigation or status bars. ```kotlin @Composable fun ScrollThresholdPaddingExample() { var list by remember { mutableStateOf(List(100) { "Item $it" }) } val lazyListState = rememberLazyListState() val reorderableLazyListState = rememberReorderableLazyListState( lazyListState = lazyListState, // Adjust scroll trigger areas to account for system bars scrollThresholdPadding = WindowInsets.systemBars.asPaddingValues(), // Optional: customize scroll threshold distance scrollThreshold = 48.dp, // Default value ) { from, to -> list = list.toMutableList().apply { add(to.index, removeAt(from.index)) } } LazyColumn( modifier = Modifier.fillMaxSize(), state = lazyListState, ) { items(list, key = { it }) { item -> ReorderableItem(reorderableLazyListState, key = item) { isDragging -> Text(item, modifier = Modifier.padding(16.dp)) } } } } ```