Is there a way to horizontally scroll only to start or specified position of previous or next element with Jetpack Compose?
Asked
Active
Viewed 2,440 times
2
-
1this is not yet supported, you can follow [this issue](https://issuetracker.google.com/issues/166590434) for updates – Phil Dukhov Aug 22 '21 at 17:14
-
1Well, yes there is. – Richard Onslow Roper Aug 22 '21 at 18:58
2 Answers
3
You can check the scrolling direction like so
@Composable
private fun LazyListState.isScrollingUp(): Boolean {
var previousIndex by remember(this) { mutableStateOf(firstVisibleItemIndex) }
var previousScrollOffset by remember(this) { mutableStateOf(firstVisibleItemScrollOffset) }
return remember(this) {
derivedStateOf {
if (previousIndex != firstVisibleItemIndex) {
previousIndex > firstVisibleItemIndex
} else {
previousScrollOffset >= firstVisibleItemScrollOffset
}.also {
previousIndex = firstVisibleItemIndex
previousScrollOffset = firstVisibleItemScrollOffset
}
}
}.value
}
Of course, you will need to create a rememberLazyListState()
, and then pass it to the list as a parameter.
Then, based upon the scrolling direction, you can call lazyListState.scrollTo(lazyListState.firstVisibleItemIndex + 1)
in a coroutine (if the user is scrolling right), and appropriate calls for the other direction.

Richard Onslow Roper
- 5,477
- 2
- 11
- 42
-
1can you provide more detailed example please how to use your code to achieve snapping behavior for lazryRow ? – Hospes Sep 02 '21 at 10:48
-
Hello Hospes, appologies for the late reply. I am afraid I do not know of the exact implementation of the same. I know it **can** be done, but exactly how, I guess you would need to reference to docs and do some serious study to grab that. Apparently the OP already knew that, so upon my pointing in the direction, he could implement it on his own. – Richard Onslow Roper Oct 10 '21 at 18:53
2
(Example for a horizontal LazyRow)
You could do a scroll to the next or previous item to create a snap effect. Check the offset of the first visible item to see which item of the list takes up more screen space and then scroll left or right to the most visible one.
@Composable
fun SnappyLazyRow() {
val listState = rememberLazyListState()
val coroutineScope = rememberCoroutineScope()
LazyRow(
state = listState,
modifier = Modifier.fillMaxSize(),
content = {
items(/*list of items*/) { index ->
/* Item view */
if(!listState.isScrollInProgress){
if(listState.isHalfPastItemLeft())
coroutineScope.scrollBasic(listState, left = true)
else
coroutineScope.scrollBasic(listState)
if(listState.isHalfPastItemRight())
coroutineScope.scrollBasic(listState)
else
coroutineScope.scrollBasic(listState, left = true)
}
}
})
}
private fun CoroutineScope.scrollBasic(listState: LazyListState, left: Boolean = false){
launch {
val pos = if(left) listState.firstVisibleItemIndex else listState.firstVisibleItemIndex+1
listState.animateScrollToItem(pos)
}
}
@Composable
private fun LazyListState.isHalfPastItemRight(): Boolean {
return firstVisibleItemScrollOffset > 500
}
@Composable
private fun LazyListState.isHalfPastItemLeft(): Boolean {
return firstVisibleItemScrollOffset <= 500
}

Lissandra Hinojosa
- 21
- 1