Closed cj3g10 closed 2 months ago
When dragging an item inside a grid, the item's offset is incorrect at certain circumstances. I'm assuming it has to do with the fact that original row in the grid is removed.
Screen_recording_20240912_120538.webm
package sh.calvin.reorderable.demo.ui import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyGridItemScope import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.shape.RoundedCornerShape 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.MaterialTheme 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.semantics.clearAndSetSemantics import androidx.compose.ui.unit.dp import sh.calvin.reorderable.ReorderableItem import sh.calvin.reorderable.ReorderableLazyGridState import sh.calvin.reorderable.demo.Item import sh.calvin.reorderable.demo.ReorderHapticFeedbackType import sh.calvin.reorderable.demo.items1 import sh.calvin.reorderable.demo.items2 import sh.calvin.reorderable.demo.rememberReorderHapticFeedback import sh.calvin.reorderable.rememberReorderableLazyGridState @OptIn(ExperimentalFoundationApi::class) @Composable fun ComplexReorderableLazyRowScreen() { val haptic = rememberReorderHapticFeedback() var listA by remember { mutableStateOf(items1) } var listB by remember { mutableStateOf(items2) } val lazyGridState = rememberLazyGridState() val reorderableLazyGridState = rememberReorderableLazyGridState(lazyGridState) { from, to -> val listAMutable = listA.toMutableList() val listBMutable = listB.toMutableList() val fromList = if (listAMutable.firstOrNull { it.id == from.key } != null) listAMutable else listBMutable val fromItem = fromList.first { it.id == from.key } if (to.key.toString().startsWith("Header")) { if (listA.firstOrNull { it.id == from.key } != null) { listBMutable.add(0, fromItem) } else { listAMutable.add(fromItem) } fromList.remove(fromItem) } else { val toList = if (listAMutable.firstOrNull { it.id == to.key } != null) listAMutable else listBMutable val toIndex = toList.indexOfFirst { it.id == to.key } fromList.remove(fromItem) toList.add(toIndex, fromItem) } listA = listAMutable listB = listBMutable haptic.performHapticFeedback(ReorderHapticFeedbackType.MOVE) } LazyVerticalGrid( modifier = Modifier.fillMaxSize(), columns = GridCells.Fixed(3), state = lazyGridState, horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), ) { item(span = { GridItemSpan(maxLineSpan) }) { Text("Header", Modifier.padding(8.dp), MaterialTheme.colorScheme.onBackground) } listOf(listA, listB).forEachIndexed { index, list -> if (index == 0) { item( span = { GridItemSpan(maxLineSpan) } ) { Text( "List $index", Modifier .fillMaxWidth() .background(MaterialTheme.colorScheme.secondaryContainer) .padding(8.dp), MaterialTheme.colorScheme.onSecondaryContainer, ) } } else if (index == 1) { item( key = "Header", span = { GridItemSpan(maxLineSpan) } ) { ReorderableItem(reorderableLazyGridState, key = "Header") { Text( "List $index", Modifier .fillMaxWidth() .background(MaterialTheme.colorScheme.secondaryContainer) .padding(8.dp), MaterialTheme.colorScheme.onSecondaryContainer, ) } } } items( items = list, key = { it.id }, contentType = { index } ) { item -> ItemCard(item, reorderableLazyGridState) } } item(span = { GridItemSpan(maxLineSpan) }) { Text("Footer", Modifier.padding(8.dp), MaterialTheme.colorScheme.onBackground) } } } @OptIn(ExperimentalFoundationApi::class) @Composable private fun LazyGridItemScope.ItemCard( item: Item, reorderableLazyGridState: ReorderableLazyGridState, ) { val haptic = rememberReorderHapticFeedback() ReorderableItem(reorderableLazyGridState, key = item.id) { val interactionSource = remember { MutableInteractionSource() } Card( onClick = {}, modifier = Modifier .height(item.size.dp) .padding(horizontal = 8.dp), interactionSource = interactionSource, ) { Column( modifier = Modifier.height(item.size.dp) .padding(horizontal = 8.dp) .background( MaterialTheme.colorScheme.primaryContainer, RoundedCornerShape(8.dp) ), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.SpaceBetween, ) { Text(item.text, Modifier.padding(horizontal = 8.dp)) IconButton( modifier = Modifier .draggableHandle( onDragStarted = { haptic.performHapticFeedback(ReorderHapticFeedbackType.START) }, onDragStopped = { haptic.performHapticFeedback(ReorderHapticFeedbackType.END) }, interactionSource = interactionSource, ) .clearAndSetSemantics { }, onClick = {}, ) { Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder") } } } } }
I'll look into this when I get up tomorrow. Thank you for opening these issues.
When dragging an item inside a grid, the item's offset is incorrect at certain circumstances. I'm assuming it has to do with the fact that original row in the grid is removed.
Screen_recording_20240912_120538.webm