Reputation: 10977
I have a compose view with a LazyList
inside. Above the list I show a text with the offset of the first item in the list.
@Composable
fun MyComponent() {
Column(modifier = Modifier.padding(vertical = 10.dp)) {
val state = rememberLazyListState()
val offset = state.firstItemOffset
Text(text = "First item offset: $offset")
Log.e("tag", "MyComponent() ... drawing with offset $offset")
LazyRow(state = state) {
items(100) {
Text(
modifier = Modifier.padding(10.dp),
text = "Item $it"
)
}
}
}
}
private val LazyListState.firstItemOffset: Int?
get() = layoutInfo.visibleItemsInfo.firstOrNull { it.index == 0 }?.offset
I noticed that when i scroll the list, MyComponent
gets recomposed, even when the first item is off screen and the returned offset is null
and did not change from the last composition. How could I set this up so it only recomposes when the value actually changes?
Bonus question: why does the firstItemOffset
getter not need a @Composable
annotation for it to update? This way, it only returns an int and should not trigger a recompose at all. To be clear, I want it to recompose when the value changes, I just wonder why the annotation is not needed.
Edit:
My guess is that it recomposed because the instance of layoutInfo
changes on every scroll even though the value returned by firstItemOffset
stays the same.
Upvotes: 1
Views: 1540
Reputation: 10977
It seems that the issue is that the return value of firstItemOffset
is not a state, but the used layoutInfo
internally is one. So every time layoutInfo
changes, a recomposition happens.
I am not sure if this is the best solution, but this works:
private val LazyListState.firstItemOffset: Int?
@Composable get() = remember {
derivedStateOf { layoutInfo.visibleItemsInfo.firstOrNull { it.index == 0 }?.offset }
}.value
Upvotes: 2