Is there a way to horizontally scroll only to start or specified position of previous or next element with Jetpack Compose?
2 Answers
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.
2 Comments
(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 }