Closed zhelenskiy closed 3 weeks ago
Oh this is very strange. I have no clue how this is happening. I will wait for your minimized example. Is this on the latest version of reorderable?
Yes
Did this work with an older version of the library? Also does this have a invisible item before the first item like in #4? I'm assuming so because I don't see a jump after moving the first item.
No, I don't have any invisible items. I didn't test for older versions. But I reproduced it successfully with a smaller example.
https://github.com/Calvin-LL/Reorderable/assets/55230817/cd95d8a7-f3ef-4410-926f-39d587f9503d
import androidx.compose.animation.*
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.MaterialTheme.colors
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Close
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import kotlinx.coroutines.launch
import sh.calvin.reorderable.ReorderableItem
import sh.calvin.reorderable.rememberReorderableLazyListState
@Composable
@Preview
fun App() {
MaterialTheme {
TabRow()
}
}
fun main() = application {
Window(onCloseRequest = ::exitApplication) {
App()
}
}
typealias Id = Long
data class Tab(val id: Id)
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun TabRow() {
var tabModels: List<Tab> by remember { mutableStateOf(listOf(Tab(-1))) }
val coroutineScope = rememberCoroutineScope()
val tabRowScrollState = rememberLazyListState()
val reorderableLazyColumnState =
rememberReorderableLazyListState(tabRowScrollState) { from, to ->
coroutineScope.launch {
tabModels = tabModels.toMutableList().apply {
add(to.index, removeAt(from.index))
}
}
}
var chosenId by remember { mutableStateOf(tabModels.first().id) }
BoxWithConstraints {
AnimatedVisibility(
visible = maxHeight > 300.dp,
enter = expandVertically(),
exit = shrinkVertically(),
) {
LazyRow(
state = tabRowScrollState,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
itemsIndexed(
tabModels,
key = { _, model -> model.id }) { index, tabModel ->
ReorderableItem(
reorderableLazyColumnState,
key = tabModel.id
) { isDragging ->
val isSelected = tabModel.id == chosenId
val chooseThis = { coroutineScope.launch { chosenId = tabModel.id } }
Row(
Modifier
.height(IntrinsicSize.Min).draggableHandle()
) {
Tab(
selected = isSelected,
enabled = !isSelected,
modifier = Modifier
.padding(horizontal = 2.dp)
.clip(RoundedCornerShape(topEnd = 4.dp, topStart = 4.dp)),
onClick = { chooseThis() },
) {
Box(Modifier.width(IntrinsicSize.Min)) {
val textColor = if (isSelected) Color.Black else Color.Blue
val maxTabWidth = 300.dp
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.widthIn(max = maxTabWidth)
.padding(vertical = 4.dp)
.padding(start = 12.dp),
) {
Spacer(Modifier.width(6.dp))
val textStyle =
TextStyle(color = textColor, fontSize = 18.sp)
Box(Modifier.weight(1f)) {
Text(
text = tabModel.id.toString(),
style = textStyle,
maxLines = 1,
softWrap = false,
modifier = Modifier
.horizontalScroll(rememberScrollState())
)
}
val expectedCloseSize by animateDpAsState(if (tabModels.size > 1) 48.dp else 8.dp)
Box(Modifier.width(expectedCloseSize)) {
androidx.compose.animation.AnimatedVisibility(
tabModels.size > 1,
enter = scaleIn() + fadeIn(),
exit = scaleOut() + fadeOut(),
) {
IconButton(
onClick = {
},
enabled = tabModels.size > 1,
) {
Icon(
imageVector = Icons.Default.Close,
tint = textColor,
contentDescription = "Close tab"
)
}
}
}
}
}
}
}
}
}
item {
IconButton(
onClick = {
coroutineScope.launch {
tabModels += Tab(tabModels.size.toLong())
}
}
) {
Icon(
Icons.Default.Add,
contentDescription = "Add tab",
)
}
}
}
}
}
}
For 2.0.0 The library seems not to be working properly at all.
https://github.com/Calvin-LL/Reorderable/assets/55230817/e729bc23-1810-4428-b031-1c3efcaac237
With 2.0.1, the previous case works well.
https://github.com/Calvin-LL/Reorderable/assets/55230817/c2ae048b-5a6e-4f5a-b80a-63857bde593c
Thank you! I will take a look.
Just for my internal planning, when are you going to take a look at the issue?
Just got up. I think I see what was causing it last night, something in my library is causing your composable to constantly recompose. I will fix it in the next 48 hours but likely sooner. Sorry about the long timeline, I'm playing DEF CON CTF Qualifier this weekend.
Well, my experience shows that average ETA of response in GitHub issues is several days in the best case and several weeks or months in the others. So, having responses within several minutes (7 minutes for the first reply and several for the rest) is insanely fast. Your eta for the fix is also tens of times less than the average. So there're no complaints!
Good luck at the Qualifier! Let all the flags be with you 🙏
Just released v2.0.3! Let me know if that fixes it. Thank you again for reporting this issue 🙏.
It partially fixes. It is not stuck at the point anymore, but it stops after exchanging with the next element.
https://github.com/Calvin-LL/Reorderable/assets/55230817/7ff367df-83c4-4f24-a33d-c9e52033e51d
It partially fixes. It is not stuck at the point anymore, but it stops after exchanging with the next element.
https://github.com/Calvin-LL/Reorderable/assets/55230817/7ff367df-83c4-4f24-a33d-c9e52033e51d
Oh strange I'll take a look.
Hi, I also have the same issue (but with a vertical lazylist), and another one, I'm not sure if it's linked to this issue so I just comment here, maybe it can help: When I try to move an item to the last position, it's impossible, the item doesn't move, there is no unexpected stop but maybe the problem is the same. I can drag the last item to above, but not the before last to bellow Thank you for your time, this library is the best drag and drop lib I found, despite these issues :+1:
Hi, I also have the same issue (but with a vertical lazylist), and another one, I'm not sure if it's linked to this issue so I just comment here, maybe it can help: When I try to move an item to the last position, it's impossible, the item doesn't move, there is no unexpected stop but maybe the problem is the same. I can drag the last item to above, but not the before last to bellow Thank you for your time, this library is the best drag and drop lib I found, despite these issues 👍
That sounds like a different issue. Can you post a screen recording?
Here it is (the true/false) at the begining is just debug, I was checking "isDraging" to see if I did something wrong
https://github.com/Calvin-LL/Reorderable/assets/3226611/53e38acb-9837-45a1-bbdf-3d902abea86d
Here it is (the true/false) at the begining is just debug, I was checking "isDraging" to see if I did something wrong
Screen_recording_20240507_193401.mp4
That looks like a different issue. Does the ReorderableItem on the last item have (enable = true)? It would help me debug this if you can provide a simplified version of your code that can reproduce this bug in a new issue 🙏.
No the last one is like the other ones, nothin special:
ReorderableItem(reorderableLazyListState, key = item.metaId.hashCode()) {
I will try to make a simple example and come back here
It partially fixes. It is not stuck at the point anymore, but it stops after exchanging with the next element.
Screen.Recording.2024-05-07.at.16.30.35.mov
Should work in v2.0.4
Here it is (the true/false) at the begining is just debug, I was checking "isDraging" to see if I did something wrong
Screen_recording_20240507_193401.mp4
I'm also having this problem where an item couldn't push the bottom item up and when the first item swaps position with the second item, the first item loses the drag. Both in a vertical lazy list. And I've just tried with 2.0.4.
(Upgraded from 1.5.2, which the first item losing drag problem already existed)
Ok I made an example, AND making it I found a workaround So the bottom issue seems to only happen when the last item is at the bottom of the screen (at least on android) So what I did is add a contentPadding to the list (which is exactly what I need in the specs I have so it's ok for me) the contentPadding is commented in the code below But I still have the issue with the top item, even in 2.0.4
package com.example.testsapplication
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.ExperimentalFoundationApi
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.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.testsapplication.ui.theme.TestsApplicationTheme
import sh.calvin.reorderable.ReorderableItem
import sh.calvin.reorderable.rememberReorderableLazyListState
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TestsApplicationTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
val listState = rememberLazyListState()
var itms by remember {
mutableStateOf(
listOf(
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o")
)
}
val reorderableLazyListState = rememberReorderableLazyListState(lazyListState = listState) { from, to ->
val fromObject = itms[from.index]
val toObject = itms[to.index]
val newlist = itms.toMutableStateList()
newlist[from.index] = toObject
newlist[to.index] = fromObject
itms = newlist.toList()
}
LazyColumn(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize(),
state = listState, // contentPadding = PaddingValues(bottom = 50.dp)
) {
items(items = itms, key = { item ->
item.hashCode()
}){ item->
ReorderableItem(reorderableLazyListState, key = item.hashCode()) { isDragging ->
val elevation = if (isDragging) 4.dp else 0.dp
Surface(shadowElevation = elevation) {
Row(modifier = Modifier.fillMaxWidth().longPressDraggableHandle()) {
Text(text = "$isDragging")
Text(text = item, modifier = Modifier
.padding(10.dp)
.height(50.dp)
)
}
}
}
}
}
}
}
}
}
}
Nothing changed for me with 2.0.4. @Calvin-LL
https://github.com/Calvin-LL/Reorderable/assets/55230817/6e98bb4c-35d2-4d91-be62-89f369dfc1dd
@zhelenskiy I finally manage to reliably reproduce this bug. I have some mildly bad news. As this library currently works right now there's no way for me to fix it until Compose Foundation v1.7.0 gets into Compose Multiplatform (See #4 and #26). But you can add contentPadding = PaddingValues(horizontal = 1.dp)
to the LazyRow as a workaround.
Compose Foundation 1.6.0-alpha08 to 1.6.0 took almost 3 months, the latest Compose Multiplatform 1.6.2 uses Compose Foundation 1.6.4 which is a 20-day gap. So expect Compose Foundation v1.7.0 to be in Compose Multiplatform in around 4 months at the current speed.
When the first item swaps with the second, because of the small size difference in this case, the first item is pushed off the screen. Once the item is off screen, LazyRow disposes that item, dragging state is lost.
Adding a small content padding with contentPadding = PaddingValues(horizontal = 1.dp)
keeps a little bit of the dragging item on screen so the dragging state is not lost.
That solution is OK for me, it also works with my LazyColumn (with a top contentPadding to 1.dp), I'll wait for the new Compose Foundation just to have the final solution but for the moment that's good. thanks! :+1:
The workaround works great, solved both top and bottom issues. Would be nice if I knew this sooner. Thanks!
Thank you for the workaround! However, I have another issue. When I start moving a tab backwards and forwards a lot, it disappears at some point in time. And find it always being placed at position 1.
https://github.com/Calvin-LL/Reorderable/assets/55230817/203b4552-1e38-4b9c-a7e8-848cd42b6291
I never considered the case where an item could be dragged off the viewport. (The gesture detector keeps reporting the items position even when it's outside the window) I will fix that.
Again, just for internal planning, is there any ETA?
I'll get it sorted out before the end of this week
@zhelenskiy try v2.1.0. If you try really hard you can still get an item to lose the dragging state by dragging it wildly outside the window back and forth, that's another bug that will be fixed when Compose Foundation v1.7.0 is out. Thank you so much for finding all these bugs.
Thanks! Is it also because of the same change in 1.7.0 or is the reason different?
Yes for the same reason as above. Even with contentPadding
, if the conditions are just right, the same thing happens. It's just that when you drag the first visible item slowly, the scroll it triggers will most likely show the -1th visible item just enough to avoid losing the dragging state.
So, I'm looking for the update for the version of Foundation to become available! Thanks. Are you going to try using alpha versions for the library?
So, I'm looking for the update for the version of Foundation to become available! Thanks. Are you going to try using alpha versions for the library?
As far as I can't tell there's no way to use any other versions of Compose Foundation than the one that is bundled into Compose Multiplatform 😭. If this library was Android only then I would be able to release an alpha version with the foundation alpha.
I'm not sure, but it seems you can add the Compose dependencies explicitly, specifying their version in version catalogs. However, if there are no multiplatform artifacts, then we can do nothing 😿.
Good news! v1.7.0 is being released in June according to https://android-developers.googleblog.com/2024/05/whats-new-in-jetpack-compose-at-io-24.html, we should see v1.7.0 land in Compose Multiplatform some time in June/July.
I just released 2.4.0-alpha01 yesterday. It uses v1.7.0 so it should fix this issue. I'm waiting for Compose Multiplatform v1.7.0 to be stable to release a stable version.
Hi! I see the same issue reproducible in 2.3.0.
Can you share a screen recording?
Any chance you're using launch
in onMove
? Try removing the launch
.
https://github.com/Calvin-LL/Reorderable?tab=readme-ov-file#faq
Seems no chance.
Which version did this start happeneing? I haven't changed the core functions for a while. If this is recent, it might be an issue with compose.
I don't know, in this project I started with 2.3.0.
I'm so sorry to have to ask you once again for a minimal reproducible example 😢 . I can't seem to trigger this issue on my end.
One second, I'm polishing the rest, will supply within 30 minutes.
It took more time than expected, so I will send it later today.
Thank you ❤️
Check https://github.com/zhelenskiy/PersonalVault there are actually two artifacts of using reorderable. One is this, the second one is that some items start jumping vertically on appearance. If I remove reorderable, everything works fine.
https://github.com/user-attachments/assets/fc40a651-6e5e-4af3-bccc-fd86cb131f10
The more exact place is this one. To reproduce it, you need to add press '+' and add some folders and files in them.
Thanks. I will try to figure this out tomorrow.
I have issues with LazyRow when I add more items. The issue happens only when I add items and only when I move the first item.
https://github.com/Calvin-LL/Reorderable/assets/55230817/dabde38e-2ffd-4e35-8ab3-ebf798a0bae2
Here is working example when I move other items.
https://github.com/Calvin-LL/Reorderable/assets/55230817/740e6d75-5806-4ece-8327-cc2b205a5e6e
I'll try to figure out what happens or minimize the example later today, but if you already have any idea what happens, let me now.
The version is
2.0.1
, the platform is Desktop. I test it on Mac OS 14.4.1.