LazyColumn - Items Key parameter purpose?
Asked Answered
E

3

19

Can someone explain me what's the main purpose of the 'key' parameter inside items/itemsIndexed function of LazyListScope? What do we get or don't get if we specify that parameter? I'm not sure that I understand the official docs related to this parameter:

key - a factory of stable and unique keys representing the item. Using the same key for multiple items in the list is not allowed. Type of the key should be saveable via Bundle on Android. If null is passed the position in the list will represent the key. When you specify the key the scroll position will be maintained based on the key, which means if you add/remove items before the current visible item the item with the given key will be kept as the first visible one.

Ebersole answered 15/8, 2021 at 9:13 Comment(1)
It is fairly overcomplicatedPropagate
V
32

I think the best answer is provided by the official doc:

By default, each item's state is keyed against the position of the item in the list. 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.

To combat this, you can provide a stable and unique key for each item, providing a block to the key parameter. Providing a stable key enables item state to be consistent across data-set changes:

@Composable
fun MessageList(messages: List<Message>) {
    LazyColumn {
        items(
            items = messages,
            key = { message ->
                // Return a stable + unique key for the item
                message.id
            }
        ) { message ->
            MessageRow(message)
        }
    }
}
Vshaped answered 15/8, 2021 at 18:22 Comment(3)
What do they mean by stable key? Does this have something to do with stability in sorting algorithms?Galvan
That means it doesn't have to be randomArlin
@Galvan I believe this to mean a key that does not change throughout dataset changes. In other words, imagine you add or remove items from the list, remaining list item keys should not change after the operation.Hilltop
D
3

Short Answer

Long Answer

  • When considering what composable should be recomposed, the compose compiler fist checks each distinct composable. We can use the documentation to define distinct as:

The instance of a composable in Composition is identified by its call site. The Compose compiler considers each call site as distinct. Calling composables from multiple call sites will create multiple instances of the composable in Composition.

call site : is the source code location in which a composable is called. This influences its place in Composition, and therefore, the UI tree

  • so basically a composable is distinct if it is called in different spots in your code, even if the composables are the same:
Column {  
    YourComposable()
    YourComposable() 
    }
  • Both instance of YourComposable() are considered distinct by the compiler

  • So if a composable is distinct(called from different places in your code) the compose compiler will know to only recompose the composable that has a state change

  • However, what happens when you have a for loop like this:

@Composable
fun MoviesScreen(movies: List<Movie>) {
    Column {
        for (movie in movies) {
            // MovieOverview composables are placed in Composition given its
            // index position in the for loop
            MovieOverview(movie)
        }
    }
}

  • MovieOverview is being called from the same call site(not distinct). To combat this scenario the compose compiler will use the execution order in addition to the call site to keep the instances distinct

  • This will work fine as long as values are only removed and added to the bottom of the list(index of length-1). But once the list changes by either adding to the top or the middle of the list. Or by removing or reordering items ,it'll cause a recomposition in all MovieOverview calls whose input parameter has changed position in the list

  • if you have a list of length 100 and you insert at item at index 50, then all the composables from index 99-50 will get recomposed even though they have not had their state changed

The solution to this is the key composable

  • the key composable allows the compose compiler to identify each composable as distinct and eliminate the wasteful recompositions
@Composable
fun MoviesScreenWithKey(movies: List<Movie>) {
    Column {
        for (movie in movies) {
            key(movie.id) { // Unique ID for this movie
                MovieOverview(movie)
            }
        }
    }
}

  • The code block above can now have its items, rearranged, removed and added to at any index and no wasteful recompositions will occur.

What is the LazyColumn items key parameter purpose ?

  • It serves the exact same role. LazyColumn just has built-in support for the key composable
@Composable
fun MoviesScreenLazy(movies: List<Movie>) {
    LazyColumn {
        items(movies, key = { movie -> movie.id }) { movie ->
            MovieOverview(movie)
        }
    }
}

  • If we did not provide the key and tried to remove, add or reorganize we would face the exact same wasteful recompositions mention earlier

Resources

Lifecycle of composables documentation

Dogeared answered 10/11, 2023 at 15:15 Comment(0)
B
-1

you can use this way

@Composable
fun A(list: MutableList<Model>) {
    Column {
        LazyColumn {
            items(
                count = list.size,
                key = {
                    list[it].id
                }, itemContent = { index ->
                    Text(text = list[index].text)
                }
            )
        }
    }
}
Bloodandthunder answered 21/2, 2023 at 10:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.