An efficient way to check when a specific LazyColumn item comes into view?
Asked Answered
P

1

13

I need to check when a certain LazyColumn item comes into view, and once it does, make a callback to onItemWithKeyViewed() only once to notify that this item has been viewed. My attempt:

@Composable
fun SpecialList(
    someItems: List<Things>,
    onItemWithKeyViewed: () -> Unit
) {
    val lazyListState = rememberLazyListState()

    if (lazyListState.isScrollInProgress) {
        val isItemWithKeyInView = lazyListState.layoutInfo
            .visibleItemsInfo
            .any { it.key == "specialKey" }
        if (isItemWithKeyInView) {
            onItemWithKeyViewed()
        }
    }

    LazyColumn(
        state = lazyListState
    ) {
        items(items = someItems) { itemData ->
            ComposableOfItem(itemData)
        }
        item(key = "specialKey") {
            SomeOtherComposable()
        }
    }
}

Issue with my method is I notice the list scrolling performance degrades badly and loses frames. I realize this may be because it's checking all visible item keys on every frame?

Also, onItemWithKeyViewed() is currently being called multiple times instead of just the first time it's viewed.

Is there a more efficient way to make a single callback to onItemWithKeyViewed() only the first time "specialKey" item is viewed?

Pericranium answered 2/2, 2022 at 5:54 Comment(0)
Q
15
  1. In such cases, when you have a state that is updated often, you should use derivedStateOf: this will cause recomposition only when the result of the calculation actually changes.
  2. You should not call side effects (which is calling onItemWithKeyViewed) directly in the composable builder. You should use one of the special side-effect functions instead, usually LaunchedEffect - this ensures that the action is not repeated. You can find more information on this topic in Thinking in Compose and in side-effects documentation.
val isItemWithKeyInView by remember {
    derivedStateOf {
        lazyListState.isScrollInProgress &&
                lazyListState.layoutInfo
                    .visibleItemsInfo
                    .any { it.key == "specialKey" }
    }
}
if (isItemWithKeyInView) {
    LaunchedEffect(Unit) {
        onItemWithKeyViewed()
    }
}
Queer answered 2/2, 2022 at 6:26 Comment(1)
I want to do some call when each list item becomes visible. But it should only be done once. Ideally, I would like to avoid calling the same function as you scroll up and down. What pattern can I follow? Secondly, what is the effect of LazyColumn's recreation/recomposition of the Composables? Are the effects restarted?Inexplicit

© 2022 - 2024 — McMap. All rights reserved.