I was wondering if there is a way or a resource I could refer to in order to achieve a side effect on a LazyRow
when an item is scrolled?
The side effect is basically to call a function in the viewModel to alter the state of the list's state.
LazyRow
items with a snap behaviorSo far I have tried NestedScrollConnection
class OnMoodItemScrolled : NestedScrollConnection {
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
viewModel.fetchItems()
return super.onPostFling(consumed, available)
}
}
The issue with the above is that the side effect is going to be executed anyway even-though the item displayed after the scroll is the same as before the scroll.
I also tried to collecting the listState interaction as the following
val firstVisibleItem: Int = remember { sectionItemListState.firstVisibleItemIndex }
sectionItemListState.interactionSource.collectIsDraggedAsState().let {
if (firstVisibleItem != sectionItemListState.firstVisibleItemIndex) {
viewModel.fetchItems()
}
}
The issue with the above is that the side effect is going to be executed the second the composable is composed for the first time.
I solved my issue using a LaunchedEffect
with 2 keys.
val sectionItemListState = rememberLazyListState()
val flingBehavior = rememberSnapFlingBehavior(sectionItemListState)
var previousVisibleItemIndex by rememberSaveable {
mutableStateOf(0)
}
val currentVisibleItemIndex: Int by remember {
derivedStateOf { sectionItemListState.firstVisibleItemIndex }
}
val currentVisibleItemScrollOffset: Int by remember {
derivedStateOf { sectionItemListState.firstVisibleItemScrollOffset }
}
LaunchedEffect(currentVisibleItemIndex, currentVisibleItemScrollOffset) {
if (previousVisibleItemIndex != currentVisibleItemIndex && currentVisibleItemScrollOffset == 0) {
// The currentVisible item is different than the previous one && it's fully visible
viewModel.fetchItems()
previousVisibleItemIndex = currentVisibleItemIndex
}
}
Using both currentVisibleItemIndex
and currentVisibleItemScrollOffset
as keys will make sure that the LaunchedEffect
will be triggered whenever one of them changes. Moreover, checking if the previousVisibleItemIndex
is different than the currentVisibleItemIndex
will ensure that we only trigger this effect only if the visible item is changing.
However, this condition will true also if the use has partially scrolled and since I have a snapping effect it will go back to the previous position. Which will result in triggering the effect twice.
In order to make sure that we only trigger the effect only in case were we actually scrolled to the next/previous fully visible position we need to rely on the scrollOffset
.