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