It seems everyone already knows that in some LazyColumn you can set keys for elements and they're used so that the same composable isn't bothered unnecessarily. Looks roughly like this:
LazyColumn {
items(items, key = { it.id }) {
ListItem(it.text)
}
}
When we want to add some header here there's an absolutely natural desire to write by analogy like this:
LazyColumn {
item(key = { "header" }) { Header() }
items(items, key = { item -> item.id }) {
ListItem(it.text)
}
}
It compiles, launches, works. And then crashes in prod. 😮
Observe the beauties of the Compose API. Here's the items function (simplified):
fun <T> LazyListScope.items(
items: List<T>,
key: ((T) -> Any)? = null,
contentType: (T) -> Any? = { null },
itemContent: @Composable LazyItemScope.(T) -> Unit
)
And here's one item:
fun item(
key: Any? = null,
contentType: Any? = null,
content: @Composable LazyItemScope.() -> Unit
)
In both cases we can pass a lambda to key, because the guys from Google have Any? in parameters here. Why not. And only in a comment near the function it's cautiously written that key under the hood on Android will be saved in Bundle, so it should be either primitive, or a string, or Parcelable. And in the case of items - all the same is relevant for key().
Accordingly here you need to be incredibly careful and remember that you need to write like this:
LazyColumn {
item(key = "header") { Header() }
items(items, key = { item -> item.id }) {
ListItem(it.text)
}
}
If I were writing the item function, I would probably overload it for all types saveable in bundle. Or generally make an explicit Parcelable wrapper for the key, return it in the lambda for items too.
This is that same case for which you get slapped on the wrists in code review for Any? where it's not needed. I suspect they solved the problem of dependency on Android SDK in foundation with this hack, but it doesn't make it easier, honestly speaking.