Home > Software design >  How to know items which are fully visible in the view port of a lazy row in jetpack compose?
How to know items which are fully visible in the view port of a lazy row in jetpack compose?

Time:05-29

I have a lazy row that contains items. Now I want to make an API call for the items which are fully visible in the viewport whenever the user scrolls the lazy row.

I have tried the following code:

    listState = rememberLazyListState()
    LaunchedEffect(listState){
    snapshotFlow { listState.firstVisibleItemIndex }
    .collectLatest{
    Log.d("printed Item",  listState.firstVisibleItemIndex.toString())
    }}

The problems with this code are:

  1. Even though the 2nd item occupies the viewport, it will not be printed unless the 1st item is fully invisible.
  2. For the tablets, due to their large screen sizes, only the API call is made for the first visible item even though there are 2 visible items on the screen. Please refer to the screenshots.

When the first item is partially visible and 2nd item is fully visible [1]: https://i.stack.imgur.com/l5QcB.jpg

When the 2nd tile is fully visible and the first tile is completely invisible [2]: https://i.stack.imgur.com/6rmiQ.jpg

For the tablets where 2 items are completely visible [3]: https://i.stack.imgur.com/QYRTI.jpg

Can anyone please tell me how to resolve my issue? Thanks in advance.

CodePudding user response:

All info about visible items is available in state.layoutInfo. To check wether the item is visible you need to compare first and last item positions(all other items for sure are gonna be visible) according to viewport size.

val state = rememberLazyListState()
val fullyVisibleIndices: List<Int> by remember {
    derivedStateOf {
        val layoutInfo = state.layoutInfo
        val visibleItemsInfo = layoutInfo.visibleItemsInfo
        if (visibleItemsInfo.isEmpty()) {
            emptyList()
        } else {
            val fullyVisibleItemsInfo = visibleItemsInfo.toMutableList()

            val lastItem = fullyVisibleItemsInfo.last()

            val viewportHeight = layoutInfo.viewportEndOffset   layoutInfo.viewportStartOffset

            if (lastItem.offset   lastItem.size > viewportHeight) {
                fullyVisibleItemsInfo.removeLast()
            }

            val firstItemIfLeft = fullyVisibleItemsInfo.firstOrNull()
            if (firstItemIfLeft != null && firstItemIfLeft.offset < layoutInfo.viewportStartOffset) {
                fullyVisibleItemsInfo.removeFirst()
            }

            fullyVisibleItemsInfo.map { it.index }
        }
    }
}
LazyColumn(
    state = state,
    contentPadding = PaddingValues(30.dp)
) {
    items(100) {
        Text(
            it.toString(),
            modifier = Modifier
                .background(if (fullyVisibleIndices.contains(it)) Color.Green else Color.Transparent)
                .padding(30.dp)
        )
    }
}

Not sure how are you gonna use this info in real life, if you actually need to update item views, for performance reasons it may be better to move this logic inside each item to not trigger recomposition of the whole LazyColumn

  • Related