I have a lazycolumn with items, and I want to send an event every time one of the items appears on screen. There are examples of events being sent the first time (like here https://plusmobileapps.com/2022/05/04/lazy-column-view-impressions.html) but that example doesn't send events on subsequent times the same item reappears (when scrolling up, for example).
I know it shouldn't be tied to composition, because there can be multiple recompositions while an item remains on screen. What would be the best approach to solve something like this?
I modified example in article from keys to index and it works fine, you should check out if there is something wrong with keys matching.
@Composable
private fun MyRow(key: Int, lazyListState: LazyListState, onItemViewed: () -> Unit){
Text(
"Row $key",
color = Color.White,
modifier = Modifier
.fillMaxWidth()
.background(Color.Red)
.padding(20.dp)
)
ItemImpression(key = key, lazyListState = lazyListState) {
onItemViewed()
}
}
@Composable
fun ItemImpression(key: Int, lazyListState: LazyListState, onItemViewed: () -> Unit) {
val isItemWithKeyInView by remember {
derivedStateOf {
lazyListState.layoutInfo
.visibleItemsInfo
.any { it.index == key }
}
}
if (isItemWithKeyInView) {
LaunchedEffect(Unit) {
onItemViewed()
}
}
}
Then used it as
LazyColumn(
verticalArrangement = Arrangement.spacedBy(14.dp),
state = state
) {
items(100) {
MyRow(key = it, lazyListState = state) {
println("🔥 Item $it is displayed")
if(it == 11){
Toast.makeText(context, "item $it is displayed", Toast.LENGTH_SHORT).show()
}
}
}
}
Result
Also instead of sending LazyListState to each ui composable you can move ItemImpression above list as a Composable that only tracks events using state. I put 2, but you can send a list and create for multiple ones either
@Composable
private fun LazyColumnEventsSample() {
val context = LocalContext.current
val state = rememberLazyListState()
ItemImpression(key = 11, lazyListState = state) {
Toast.makeText(context, "item 11 is displayed", Toast.LENGTH_SHORT).show()
}
ItemImpression(key = 13, lazyListState = state) {
Toast.makeText(context, "item 13 is displayed", Toast.LENGTH_SHORT).show()
}
LazyColumn(
verticalArrangement = Arrangement.spacedBy(14.dp),
state = state
) {
items(100) {
Text(
"Row $it",
color = Color.White,
modifier = Modifier
.fillMaxWidth()
.background(Color.Red)
.padding(20.dp)
)
}
}
}