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)
)
}
}
}