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:
- Even though the 2nd item occupies the viewport, it will not be printed unless the 1st item is fully invisible.
- 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