How to check visibility of list item in Jetpack Compose

Viewed 2724

FlatList of React Nativehas a property viewabilityConfigCallbackPairs where you can set:

viewabilityConfig: {
    itemVisiblePercentThreshold: 50,
    waitForInteraction: true,
  }

to detect visible items of the list with threshold of 50% and after interaction or scroll.

Does Jetpack Compose also have something similar to this?

There is LazyListState with some layout info. But I wonder if there is anything built-in component/property for this use case.

Edit

I have a list of cardviews and I want to detect which card items (at least 50% of card is visible) are visible on display. But it needs to be detected only when the card is clicked or list is scrolled by user.

1 Answers

To get an updating list of currently visible items with a certain threshold LazyListState can be used.

LazyListState exposes the list of currently visible items List<LazyListItemInfo>. It's easy to calculate visibility percent using offset and size properties, and thus apply a filter to the visible list for visibility >= threshold.

LazyListItemInfo has index property, which can be used for mapping LazyListItemInfo to the actual data item in the list passed to LazyColumn.

fun LazyListState.visibleItems(itemVisiblePercentThreshold: Float) =
    layoutInfo
        .visibleItemsInfo
        .filter {
            visibilityPercent(it) >= itemVisiblePercentThreshold
        }

fun LazyListState.visibilityPercent(info: LazyListItemInfo): Float {
    val cutTop = max(0, layoutInfo.viewportStartOffset - info.offset)
    val cutBottom = max(0, info.offset + info.size - layoutInfo.viewportEndOffset)

    return max(0f, 100f - (cutTop + cutBottom) * 100f / info.size)
}

Usage

val list = state.visibleItems(50f) // list of LazyListItemInfo

This list has to be mapped first to corresponding items in LazyColumn.

val visibleItems = state.visibleItems(50f)
            .map { listItems[it.index] }

@Composable
fun App() {
    val listItems = remember { generateFakeListItems().toMutableStateList() }

    val state = rememberLazyListState()

    LazyColumn(Modifier.fillMaxSize(), state = state) {
        items(listItems.size) {
            Item(listItems[it])
        }
    }

    val visibleItems = state.visibleItems(50f)
        .map { listItems[it.index] }

    Log.d(TAG, "App: $visibleItems")
}

fun generateFakeListItems() = (0..100).map { "Item $it" }
Related