'Android Compose LazyColumn IllegalArgumentException: Key was already used

I keep getting IllegalArgumentException: Key was already used. If you are using LazyColumn/Row please make sure you provide a unique key for each item.

I am sure my keys are unique. Here is my sample code:

@Composable
fun UiComponent() {
    LazyColumn(
                verticalArrangement = Arrangement.spacedBy(12.dp),
                state = scrollState,
                reverseLayout = true
            ) {
                items(
                    items = viewmodel.messages,
                    key = { item -> item.hashcode() },
                    itemContent = { item: Entity ->
                        if (item.isDeleted) {
                            //show deleted ui
                        } else {
                            //show messages
                        }
                    })
            }
        }
}

ViewModel {

    init { 
       observeDataFromDB()
    }

    private val _messages: MutableList<Entity> = mutableStateListOf()
    val messages: List<Entity> = _messages


    fun observeDataFromDB() {
        viewModelScope.launch {
            repo.getData().collect {
                _messages.apply {
                    addNewItem(it)
                }
            }
        }
    }

}


//extensions
fun MutableList<Entity>.addNewItem(entity: Entity) {
    if (this.size >= MAX_SIZE) {
        removeLast()
    }
    Log("existing ${this.toList().map { "${it.hashCode()}" }}")
    Log("adding new ${entity.hashCode()}")
    this.add(0, entity)
}

//id is unique
data class Entity(id:String, isDeleted: Boolean, message: String)

I have also tried providing an observable from my Viewmodel(StateFlow<List<Entity>) but I get the same error. Also tried setting id as the key.

Mostly encountering this issue when items are rapidly added. For example user spamming multiple messages in a short time.



Solution 1:[1]

If any of your messages are identical, you'll end up with the same hashcode value and this will generate the error. To avoid this, if your messages don't have an id, use itemsIndexed instead of just items and assign the index as the key:

@Composable
fun UiComponent() {
    LazyColumn(
                verticalArrangement = Arrangement.spacedBy(12.dp),
                state = scrollState,
                reverseLayout = true
            ) {
                items(
                    items = viewmodel.messages,
                    key = { item -> item.id },
                    itemContent = { item: Entity ->
                        if (item.isDeleted) {
                            //show deleted ui
                        } else {
                            //show messages
                        }
                    })
            }
        }
}

Solution 2:[2]

I think problem is every enter the composable list not recraeting instead of fetching the same list thats why probably app getting crash

Solve: You can create disposable effect when every leave the composable you can use this method List.clear() on OnDispose{}

Hope solve your problem :)

Solution 3:[3]

If anyone is facing similar issue:

For me it got resolved after removing mutableStateListOf and using a simple list and observing this list in composable as state.

Basically StateFlow<List< Entity>> and in compose viewmodel.message.collectAsState(). Finally passing this list to the lazyColumn

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 sinan
Solution 3 Ruben Quadros