-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Item loses drag state when dragged into a different list. #49
Comments
Thanks for the code. I'll take a look. |
I see, I guess I've never thought much about this use case. It seems like compose's Here's a piece of code that does what you're thinking of. I'll add an example in the README and demo app for this. Screen_recording_20240911_154348.mp4package 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.Row
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.LazyColumn
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.lazy.items
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.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.ReorderableLazyListState
import sh.calvin.reorderable.demo.Item
import sh.calvin.reorderable.demo.ReorderHapticFeedbackType
import sh.calvin.reorderable.demo.rememberReorderHapticFeedback
import sh.calvin.reorderable.rememberReorderableLazyListState
@Composable
fun ComplexReorderableLazyColumnScreen() {
TestMultipleList()
}
val items1 = (0..3).map {
Item(id = 1000 + it, text = "Group A #$it", size = if (it % 2 == 0) 70 else 100)
}
val items2 = (0..3).map {
Item(id = 2000 + it, text = "Group B #$it", size = if (it % 2 == 0) 70 else 100)
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun TestMultipleList() {
val haptic = rememberReorderHapticFeedback()
var listA by remember { mutableStateOf(items1) }
var listB by remember { mutableStateOf(items2) }
val lazyListState = rememberLazyListState()
val reorderableLazyColumnState = rememberReorderableLazyListState(lazyListState) { 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 == "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)
}
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = lazyListState,
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
listOf(listA, listB).forEachIndexed { index, list ->
if (index == 1) {
item(key = "Header") {
ReorderableItem(reorderableLazyColumnState, key = "Header") {
Text(
"List $index",
Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.secondaryContainer)
.padding(8.dp),
MaterialTheme.colorScheme.onSecondaryContainer,
)
}
}
}
items(
items = list,
key = { it.id },
) { item ->
ItemCard(item, reorderableLazyColumnState)
}
}
// The following doesn't work for some reason
// items(
// items = listA,
// key = { it.id },
// ) { item ->
// ItemCard(item, reorderableLazyColumnState)
// }
// item(key = "Header") {
// ReorderableItem(reorderableLazyColumnState, key = "Header") {
// Text(
// "List $1",
// Modifier
// .fillMaxWidth()
// .background(MaterialTheme.colorScheme.secondaryContainer)
// .padding(8.dp),
// MaterialTheme.colorScheme.onSecondaryContainer,
// )
// }
// }
// items(
// items = listB,
// key = { it.id },
// ) { item ->
// ItemCard(item, reorderableLazyColumnState)
// }
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LazyItemScope.ItemCard(item: Item, reorderableLazyColumnState: ReorderableLazyListState) {
val haptic = rememberReorderHapticFeedback()
ReorderableItem(reorderableLazyColumnState, key = item.id) {
val interactionSource = remember { MutableInteractionSource() }
Card(
onClick = {},
modifier = Modifier
.height(item.size.dp)
.padding(horizontal = 8.dp),
interactionSource = interactionSource,
) {
Row(
Modifier.fillMaxSize(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
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")
}
}
}
}
} |
Update: the code that is commented out above under |
Thanks for your investigation. I had a hunch that the item was being disposed but I had no idea why. In fact, I still don't really understand why your solution of using I'm also not sure what you mean by the commented lines work in Compose for Android. My actual project is using AndroidX Compose and not Compose Multiplatform, and the commented lines don't work for me. For reference, I'm using the latest BOM so my Compose version is 1.7. My actual use case is a grid instead of a list, and after making adjustments in your sample code, I can get this reordering between two lists to work on a And one more thing, when using reordering with a grid, I encountered a UI issue when dragging. But since this issue is unrelated to this, I'll create another thread for this issue. #50 (comment) |
Hmm very interesting. I started an new Android project with the latest BOM and it works fine. I also have no clue why one works and the other doesn't since they should be equivalent. |
And I don't know if there's much I can do here since it seems like a compose bug and I can't stop it from being disposed. |
Don't worry about it. I just resolved my issue; the issue has nothing to do with Thanks for the help. BTW, do you know why your solution of using |
I have no clue. I've been experimenting with different versions of everything to see when the commented out part works/breaks and still haven't found anything. I'm very curious too, it breaks my whole mental model of how compose works. |
I've created an issue on the Google Issue Tracker: https://issuetracker.google.com/issues/366123428 |
I have two list of items inside the same Lazy Layout, and I am trying to set it up so that I can drag an item from one list to the other list.
However when ever I drag an item over to the other list, it seems that the item loses the drag state.
Screen_recording_20240911_172711.webm
Here is the source code for the above demo:
The text was updated successfully, but these errors were encountered: