I am wondering if it is possible to get observer inside a @Compose
function when the bottom of the list is reached (similar to recyclerView.canScrollVertically(1)
)
Thanks in advance.
I am wondering if it is possible to get observer inside a @Compose
function when the bottom of the list is reached (similar to recyclerView.canScrollVertically(1)
)
Thanks in advance.
you can use rememberLazyListState()
and compare
scrollState.layoutInfo.visibleItemsInfo.lastOrNull()?.index == scrollState.layoutInfo.totalItemsCount - 1
How to use example:
First add the above command as an extension (e.g., extensions.kt
file):
fun LazyListState.isScrolledToEnd() = layoutInfo.visibleItemsInfo.lastOrNull()?.index == layoutInfo.totalItemsCount - 1
Then use it in the following code:
@Compose
fun PostsList() {
val scrollState = rememberLazyListState()
LazyColumn(
state = scrollState,),
) {
...
}
// observer when reached end of list
val endOfListReached by remember {
derivedStateOf {
scrollState.isScrolledToEnd()
}
}
// act when end of list reached
LaunchedEffect(endOfListReached) {
// do your stuff
}
}
For me the best and the simplest solution was to add LaunchedEffect
as the last item in my LazyColumn
:
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(someItemList) { item ->
MyItem(item = item)
}
item {
LaunchedEffect(true) {
//Do something when List end has been reached
}
}
}
Starting from 1.4.0-alpha03
you can use LazyListState#canScrollForward
to check if you are at the end of the list.
Something like:
val state = rememberLazyListState()
val isAtBottom = !state.canScrollForward
LaunchedEffect(isAtBottom){
if (isAtBottom) doSomething()
}
Before this release you can use the LazyListState#layoutInfo
that contains information about the visible items. Note the you should use derivedStateOf
to avoid redundant recompositions.
Use something:
@Composable
private fun LazyListState.isAtBottom(): Boolean {
return remember(this) {
derivedStateOf {
val visibleItemsInfo = layoutInfo.visibleItemsInfo
if (layoutInfo.totalItemsCount == 0) {
false
} else {
val lastVisibleItem = visibleItemsInfo.last()
val viewportHeight = layoutInfo.viewportEndOffset + layoutInfo.viewportStartOffset
(lastVisibleItem.index + 1 == layoutInfo.totalItemsCount &&
lastVisibleItem.offset + lastVisibleItem.size <= viewportHeight)
}
}
}.value
}
The code above checks not only it the last visibile item == last index
in the list but also if it is fully visible (lastVisibleItem.offset + lastVisibleItem.size <= viewportHeight
).
And then:
val state = rememberLazyListState()
var isAtBottom = state.isAtBottom()
LaunchedEffect(isAtBottom){
if (isAtBottom) doSomething()
}
LazyColumn(
state = state,
){
//...
}
I think, based on the other answer, that the best interpretation of recyclerView.canScrollVertically(1)
referred to bottom scrolling is
fun LazyListState.isScrolledToTheEnd() : Boolean {
val lastItem = layoutInfo.visibleItemsInfo.lastOrNull()
return lastItem == null || lastItem.size + lastItem.offset <= layoutInfo.viewportEndOffset
}
Simply use the firstVisibleItemIndex
and compare it to your last index. If it matches, you're at the end, else not. Use it as lazyListState.firstVisibleItemIndex
Found a much simplier solution than other answers. Get the last item index of list. Inside itemsIndexed of lazyColumn compare it to lastIndex. When the end of list is reached it triggers if statement. Code example:
LazyColumn(
modifier = Modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
itemsIndexed(events) { i, event ->
if (lastIndex == i) {
Log.e("console log", "end of list reached $lastIndex")
}
}
}