androidkotlinandroid-jetpack-composekotlin-flowandroid-jetpack-compose-list

Item index inside rememberDismissState not being updated on list update in LazyColumn itemsIndexed


I have a LazyColumn with some items where you can only remove the first item in the list (index 0).

The LazyColumn looks something like this:

LazyColumn {
    itemsIndexed(items, key = { _, item -> item.id }) { index, item ->
        val dismissState = rememberDismissState(DismissValue.Default) { dismissValue ->
            if (index != 0 && dismissValue == DismissValue.DismissedToEnd) {
                someOtherStuff()
                false
            } else true
        }
        if (dismissState.isDismissed(DismissDirection.StartToEnd)) {
            delete(item)
        }
        MyItem(dismissState)
    }
}

The operation of delete()is fairly irrelevant, all you have to know is that it removes the list item from the room database table, which is observed by the ViewModel, thus making items smaller by one element.

The problem is, however, that index (and lastIndex) inside the rememberDismissState is not up-to-date with the actual index of the itemsIndexed. If I remove the first item in a list of 3 items, I won't be able to remove the first item again in the resulting list of two items, because the index of the new first item is still 1 (or so rememberDismissStatethinks).

I know that most likely this has something to do with the rememberDismissState not being recalculated when the list changes, so how do I do that? A workaround would be to map the list to indices before putting it into itemsIndexed, but is there any way to make the code above "work properly"?


Solution

  • After coming back to this problem a few months later, I realized the issue:

    The rememberDismissState "remembers" the list state and does not recompute even when the list changes. Since I need this to happen to calculate index or items.size, I fixed it by converting rememberDismissState into a keyed remember, like such:

    val dismissState = remember(items) { DismissState(DismissValue.Default) {...} }
    

    Thus, the dismissState is also recalculated each time the list changes, which was needed for me to get the code in my initial question working.

    Additionally you could use derivedStateOf as described here, but as discussed in that answer, semantically there is no difference.