Home > Mobile >  Is Compose's swipe-to-dismiss state always remember the old item based on id, even the list has
Is Compose's swipe-to-dismiss state always remember the old item based on id, even the list has

Time:01-08

I have a simple example app that can

  • Load a new list (of 2 items, with id 0 and 1, and random text for each)
  • It can swipe to dismiss any item.

If I

  • load the new list for the first time
  • swipe to delete the first item
  • load a new list (that has same ID, but different random text)
  • swipe to delete the second item

It will crash as shown in the GIF below (You can get the code design from here enter image description here

The reason is crashes because, upon Swipe-to-Dismiss the 2 item (of the 2nd time loaded data), the item it found is still the 2 item of the 1st time loaded data.

It does seems dismissState (as shown code below) always remember the 1st time loaded data (instead of the new data loaded)

                val dismissState = rememberDismissState(
                    confirmStateChange = {
                        Log.d("Track", "$item\n${myListState.value.toMutableList()}")
                        viewModel.removeItem(item)
                        true
                    }
                )

Hence this causes the deletion to send the wrong item in for deletion, and thus causes the failure and crash.

The complete LazyColumn and SwipeToDismiss code is as below

       LazyColumn(modifier = Modifier.fillMaxHeight()) {
            items(
                items = myListState.value,
                key = { todoItem -> todoItem.id }
            ) { item ->
                val dismissState = rememberDismissState(
                    confirmStateChange = {
                        viewModel.removeItem(item)
                        true
                    }
                )

                SwipeToDismiss(
                    state = dismissState,
                    background = {
                        dismissState.dismissDirection ?: return@SwipeToDismiss
                        Box(modifier = Modifier.fillMaxSize().background(Color.Red))
                    },
                    dismissContent = {
                        // The row view of each item
                    }
                )
            }
        }

Is this

  1. My issue, is that I miss out on anything to refresh the dismissState upon loading of new data?
  2. A Google Bug, where SwipeToDismiss will always have to work with a list of Unique IDs . Even if the list is refreshed to a new list, it cannot have the same ID that colide with any item of the previous list
    • i.e. if I replace key = { todoItem -> todoItem.id } with key = { todoItem -> todoItem.title }, then it will all be good

CodePudding user response:

rememberDismissState() will remember the confirmStateChange lambda, which is part of the DismissState. In your case, item can change, but the lambda only captures the initial item value, leading to the crash.

You can use rememberUpdatedState to solve this:

val currentItem by rememberUpdatedState(item)
val dismissState = rememberDismissState(
    confirmStateChange = {
        viewModel.removeItem(currentItem)
        true
    }
)
  • Related