Home > OS >  Compose LazyColumn key, messes up scrolling when sorting the items
Compose LazyColumn key, messes up scrolling when sorting the items

Time:11-05

I'm trying to implement simple sort with items that are in a state.

This will work perfectly, only problem is that the animation is now gone, because it doesn't have a key.

LazyColumn(
    state = listState,
) {
    items(items.size) { index ->
        val ticker = items[index]
        Card(
            modifier = Modifier
                .fillMaxWidth()
                .animateItemPlacement(), // This won't work
        ) {
            Item(...)
        }
    }
}

But If I try to change it to:

items(items.size, key = { items[it].name }) { index ->

or If I use:

items(items, key = { items[it].name }) { item ->

It will have animations, but it will move items above and below current scroll position (even if you don't move). The wanted result is that you stay on top of the items, because we didn't move.

This is in a viewModel:

private val _state = MutableStateFlow<Response<List<Item>>>(Response.Loading)
val state get() = _state
private fun updateList() {
    viewModelScope.launch(Dispatchers.IO) {
        client.getItems()
            .onSuccess {
                unFilteredList = it.toMutableList()

                val filteredList = filterList()
                _tickerListState.value = Response.Success(filteredList)
            }
            .onFailure {}
    }
}

CodePudding user response:

... only problem is that the animation is now gone, because it doesn't have a key.

I created a sample alphabet letter sorting/filtering and yes, without an item key the animation stops working. Though I'm not sure why in your case you said having this makes the animation work, it doesn't work on mine however.

items(items) { item ->

But anyway, I'll try to make assumptions, so please bare with me.

If you think of it, without animation, any changes made on the data structure is immediately rendered to the UI, it will just simply display the new data structure, but if you add some delay/duration(animation) to those rendering changes, say sorting (e.g. switching the first and last item position):

  • Fade out last positioned item and Fade it in to first position
  • Fade out first positioned item and Fade it in to last position

the framework has to have something that can uniquely identify those position(e.g. LazyColumn's item key) so it won't mess up while doing the animation rendering, also maybe to avoid performing some overhead just to keep track of the changes.

And as per the API documentation, it needs a key for it to animate

enter image description here

The wanted result is that you stay on top of the items, because we didn't move.

Not sure if I understand this correctly, but if you want to stay on the first item, you can utilize a LaunchedEffect and use the list's size as its key to execute scrolling operations to the LazyColumn every filtering.

LaunchedEffect(key1 = list.size) {
    lazyListState.animateScrollToItem(0) // scrolls to first item
}

CodePudding user response:

Fixed the problem with:

LaunchedEffect(items.first()) {
    listState.animateScrollToItem(0)
}
LazyColumn{
        items(items, key = { it.id}) { item-> ...
}

Sometimes the animation skips, but it works 90% of the time.

  • Related