SimonSays
SimonSays

Reputation: 10977

Compose recomposes even though state value does not change

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

Answers (1)

SimonSays
SimonSays

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

Related Questions