LazyColumn is showing wrong display when deleting an item
Asked Answered
K

1

4

I'm creating an app that, among other things, enables the user to add a Consumer, and then remove him later. The consumers are shown in cards with a remove button in the end.

enter image description here

Adding a consumer works fine. However, when I try to remove a consumer, the one removed in the app screen is always the last one. I know this is not a logic implementation mistake, because I stopped the Debugger right before the items() call, and in any recomposition the list holding the consumers has the correct consumer removed! The following image shows the result after clicking the Remove button from the "B" card (the card removed is "C"!):

Consumer removed should be B

Look what the debugger shows right before the recomposition takes place:

enter image description here

The relevant code is below.

The ViewModel and Model (relevant part) definitions:

class ConsumidoresViewModel : ViewModel() {
    var lista = mutableStateListOf<Consumidor>()

    fun add(consumidor: Consumidor){
        lista += consumidor
    }

    fun remove(consumidor: Consumidor){
        lista.remove(consumidor)
    }
}

data class Consumidor(var nome: String)
    ...

The main composable, called directly from .onCreate():

fun UsersView() {
    var consumidores: ConsumidoresViewModel = viewModel()
    
    ...
    
    LazyColumn() {
        items(items = consumidores.lista) { consumidor ->
            CardNome(consumidor, consumidores)
        }
    }

The fucntion call of the Remove button:

IconButton(onClick = { consumidorViewModel.remove(consumidor) }) { ... }

I can't figure out what I'm doing wrong. I'm fairily new with Android Programming / Compose, but I have been programming for decades (not professionaly). Can someone point me to a direction? It probably has something to do with my Sates / View Model implementation, but I can't find out what, as the SnapshotStateList on the debugger clearly shows "A" and "C" cards present, and "B" gone!

Kopple answered 25/10, 2022 at 6:28 Comment(1)
Have you tried removing an instance of Consumidor from the list based on something that's unique to it? like consumidor.Id?Barm
B
5

Based on the official docs.

By default, each item's state is keyed against the position of the item in the list or grid. However, this can cause issues if the data set changes, since items which change position effectively lose any remembered state. If you imagine the scenario of LazyRow within a LazyColumn, if the row changes item position, the user would then lose their scroll position within the row.

So it's usually a good set up when your data class has a unique property like an id if you plan to manipulate a collection of it (like your removal operation), you can then use it as a key = {...} for the LazyColumn so it knows not to use the index as a unique identifier for its item elements, and that could be the reason why your'e having a wrong display of items after removing an element from the list.

LazyColumn() {
    items(items = consumidorList, key = { it.id }) { consumidorItem ->
        ...
    }
}

Update:

Linking my another answer for a movableContentOf{...} sample.

Barm answered 25/10, 2022 at 6:43 Comment(1)
I really don't understand the concept behind this, but using an unique parameter as key worked! I find it very weird that the item being deleted from the list has always been the correct one, but the UI was wrong.Kopple

© 2022 - 2024 — McMap. All rights reserved.