Why my app crashes when I run this code:
val scrollState = rememberLazyListState(0)
var isLastItemVisible by remember { mutableStateOf(false) }
LaunchedEffect(scrollState) {
while (!isLastItemVisible) {
val lastVisibleItemIndex = scrollState.layoutInfo.visibleItemsInfo.lastOrNull()?.index
val totalItemsCount = scrollState.layoutInfo.totalItemsCount
isLastItemVisible = lastVisibleItemIndex != null && lastVisibleItemIndex == totalItemsCount - 1
}
onLastItemSeen()
}
When I add any delay at last line in loop there is everything right. I display items in LazyRow.
It's giving ANR not crashing because you are in an infinite loop you can't scroll.
You can use derivedStateOf to check if last item is visible as
val isLastItemVisible by remember {
derivedStateOf {
val lastVisibleItemIndex = scrollState.layoutInfo.visibleItemsInfo.lastOrNull()?.index
val totalItemsCount = scrollState.layoutInfo.totalItemsCount
lastVisibleItemIndex != null && lastVisibleItemIndex == totalItemsCount - 1
}
}
LaunchedEffect(isLastItemVisible){
if (isLastItemVisible){
// Send event here
}
}
or snapshotFlow inside LaunchedEffect that sends event only once
// Sends event only once
LaunchedEffect(scrollState) {
snapshotFlow {
scrollState.layoutInfo
}
.map {
val lastVisibleItemIndex = it.visibleItemsInfo.lastOrNull()?.index
val totalItemsCount = it.totalItemsCount
lastVisibleItemIndex != null && lastVisibleItemIndex == totalItemsCount - 1
}
// If it's a one time event filtering it
.filter { it }
.distinctUntilChanged()
.collect {
println("New Event sent...")
}
}
Or one that sends event every time last item is visible
// Sends event every time last item is seen
LaunchedEffect(scrollState) {
snapshotFlow {
scrollState.layoutInfo
}
.map {
val lastVisibleItemIndex = it.visibleItemsInfo.lastOrNull()?.index
val totalItemsCount = it.totalItemsCount
lastVisibleItemIndex != null && lastVisibleItemIndex == totalItemsCount - 1
}
.distinctUntilChanged()
.collect {visible->
println("Scroll visible: $visible")
if (visible){
println("New Event sent...")
}
}
}
Also i add a sample to display button on bottom end when last item is visible can be implemented such as
@Preview
@Composable
private fun ListSample() {
val scrollState = rememberLazyListState(0)
val isLastItemVisible by remember {
derivedStateOf {
val lastVisibleItemIndex = scrollState.layoutInfo.visibleItemsInfo.lastOrNull()?.index
val totalItemsCount = scrollState.layoutInfo.totalItemsCount
lastVisibleItemIndex != null && lastVisibleItemIndex == totalItemsCount - 1
}
}
Box {
LazyColumn(
state = scrollState
) {
items(20) {
Text(
text = "Row $it",
modifier = Modifier.fillMaxWidth().padding(8.dp),
fontSize = 20.sp
)
}
}
if (isLastItemVisible) {
FloatingActionButton(
modifier = Modifier.align(Alignment.BottomEnd).padding(8.dp),
onClick = {
},
content = {
Icon(imageVector = Icons.Default.ArrowUpward, contentDescription = null)
}
)
}
}
}