android-jetpacklazycolumn

Why can't I use the field of the data class as the key of the items of the lazyColumn in jetpack compose?


A very very simple example (implementation(platform("androidx.compose:compose-bom:2023.08.00"))):

package com.study.myapplication

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.study.myapplication.ui.theme.MyApplicationTheme

data class Id(val level: Int, val index: Int)

class Man(val id: Id, val name: String)

val men = listOf(
    Man(Id(1, 1), "Amy"),
    Man(Id(1, 2), "Bob")
)

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Greeting(men)
                }
            }
        }
    }
}

@Composable
fun Greeting(men: List<Man>, modifier: Modifier = Modifier) {
    LazyColumn (modifier = modifier) {
        items(men, key = { it.id.index }) {   // <- render problem
        //items(men, key = { it.id }) {       // <- okay
            Text(it.name)
        }
    }
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    MyApplicationTheme {
        Greeting(men)
    }
}

Refer to the comments in the code: If I use man.Id.index as key, it renders ok. If I use man.Id as key, it prompts render problem.

In a physic device, I found the real reason:

java.lang.IllegalArgumentException: Type of the key Id(level=1, index=1) is not supported. On Android you can only use types which can be stored inside the Bundle.

Why?


Solution

  • When you use it.id.index as the key, you're essentially using an Int value, which is a primitive type that can be easily stored in a Bundle. But, when you use it.id, you're trying to use a custom data class id, which cannot be directly stored in a Bundle because it's not a primitive type nor a type that implements Parcelable.

    So If needed, you can utilize Parcelable.

    @Parcelize
    data class Id(val level: Int, val index: Int): Parcelable
    

    do not forget add kotlin-parcelize plugin to build.gradle

    plugins {
        id("kotlin-parcelize")
    }