Looking for an approach to trigger an event in a l...
# compose
m
Looking for an approach to trigger an event in a lazy column when a card is scrolled into view. At the moment, i am using a LaunchedEffect (though suspect SideEffect would be enough). Let’s for now at least assume the composition doesn’t change over time, just scrolls on and off screen. I’m wondering if this is the right approach (see thread for code)
Copy code
@Composable
private fun NumberCard(modifier: Modifier = Modifier, index: Int, onImpression: (Int) -> Unit) {
    LaunchedSideEffect("NumberCard $index") {
        onImpression(index)
    }

    Card(modifier = Modifier.fillMaxWidth()) {
        Text(text=index.toString())
    }
}
PS: I’m pretty sure i could use LazyListState with a LaunchedEffect for this, but it would probably be quite painful to code that to figure out which items are scrolled on and off screen, since it would have to have knowledge about the list of items, and that breaks down when you have then hand coded (rather than in a list). Also, LazyListState doesn’t have listeners or a flow to collect, so i’d essentially have to do while true loop and put short delays in there to check if the state has changed. not ideal.
a
you can use
snapshotFlow
to get a flow from any snapshot-observable object, fwiw
definitely don't write polling loops for observable objects 🙃
`LaunchedEffect`s run when they're present in the composition, which is different from strict visibility. LazyColumn will compose off-screen items to have them ready for performance when scrolling, so using this for impression tracking isn't going to be exact. You may or may not need, "exact" depending on your requirements.
Never count on an assumption that something in your UI won't recompose to enforce correct behavior of your code. Compose can recompose your UI because it feels like it, or because it's Tuesday. 🙂 While this may affect performance, it should never affect correctness.
m
@Adam Powell so what would you recommend if i really want “exact” logging of these impressions?
And i think snapshotFlow will help me with some other stuff i’m working on, though i need to be able to track previous state for that.
a
if you want it to be exact it's going to be a tradeoff of accuracy vs. scalability.
Modifier.onGloballyPositioned {}
will give you access to the size and position when it changes, even within other containers. Intersect that against your viewport and you have the data you need
sample code at that link
e
@Adam Powell Hopefully this thread is not dead. I have a similar requirement, but for when the element is scrolled out of the screen. I notice that as soon as the last pixels of an element is out of the screen,
onGloballyPositioned
is not called anymore. I have no idea how to record that event (it is easier when the element is scroll into screen, since the callback is called). Can you suggest how can I observe this event?
a
In the lazy layouts once something is scrolled out of the viewport it is discarded and no longer exists so it no longer receives position updates. I don't think we have any event callbacks for when a layout node is detached. Generally LaunchedEffect/DisposableEffect giving a clear signal that a composable has left the composition is close enough, but if you have a concrete use case for layout node attach/detach then please file a feature request
e
@Adam Powell Thanks, a bit late but I propose a request here https://issuetracker.google.com/issues/210040430 . It is understandable that one should consider the DisposableEffect, I will invest into that too, but hopefully supporting an accurate signal is possible.