hi all. I've a LazyRow of Card composables. I've i...
# compose
a
hi all. I've a LazyRow of Card composables. I've implemented snapping behavior successfully. now I want to execute code when user navigates between the cards in the lazy row - how can I achieve this?
s
Observe the LazyListState, and act on it. Inside there there must be information about the current first item.
👍 1
a
So I tried using
LazyListState.firstVisibleItemIndex
and I am unable to get a behavior where if a user flings between Cards composables in the LazyRow the code will execute at each fling using
LazyListState.firstVisibleItemIndex
- any thoughts where I can execute the code? Within the LazyRow body before calling the Card composable? If so, how do I detect when the user has changed the state by flinging to a different Card composable to thus execute the code? I guess it has to be done in a way that triggers recomposition so a state needs to be kept.
s
Copy code
val lazyListState = rememberLazyListState()
LaunchedEffect(lazyListState) {
  snapshotFlow { lazyListState.firstVisibleItemIndex }
    .collect { firstVisibleItemIndex ->
      println(firstVisibleItemIndex)
    }
}
If you do this, do you get emissions on each time the
firstVisibleItemIndex
changes?
a
I'm trying to run the code within the LazyRow and I get:
Copy code
@Composable invocations can only happen from the context of a @Composable function
should I run outside of it? the caller would still be a Composable
Placing the code outside (before calling the composable) of LazyRow won't trigger anything. I don't see
firstVisibleItemIndex
being logged when I fling between Cards in the LazyRow.
Commented one line of code and it worked, thanks! The problem I now have is that the index doesn't correspond to the list of Cards I populate with (in order). Therefore, for the 3rd Card I get an index of 1 when it should be 0. Have you encountered this?
s
What does the UI look like in that state? The index is of the first visible one. If your snapping config makes it so that items snap in a way where the other items are still visible, then yeah the first visible won't be your snapped item, but just the first item which is still on the screen
a
Indeed, it seems the first Card in the LazyRow is being ignored altogether from indexing. The indexing starts at 0 but with the second Card and goes on until the last Card. This has the effect that the last Card is not taken into account (means, it can not be navigated to by scrolling to it in the LazyRow of Card composables: there's the effect that index of 2 instead of 3 is being used when last Card composable in the LazyRow is scrolled to).
I was expecting the first card to be taken into account as well and index it as 0 and the rest onward by incrementing with 1. Strange behavior. How can I achieve this?
s
I can't quite follow what you are trying to achieve. Some screenshots of the state you are in and what you'd expect to happen might help me
a
Sure. I have a list of objects. I am looping through them and keeping an index. If the index from lazyListState matches the index of the event, I'm updating a Google Map cameraPositionState such that the map focuses on the object selected through scrolling of the LazyRow with Card composables (see screenshots). The state takes into account starting from 2nd composable. Thus when I scroll back to the very first Card in the LazyRow, nothing happens (it should've updated the map to focus on the very first object). Likewise, when I scroll to the very last Card in the LazyRow, the map focuses on the n - 1 (assuming n starts from 0) object in the list (it should've focused on the very last object in the list). The behavior is that the first Card in the LazyRow is unused and the last Card doesn't point to the last element when scrolling through the LazyRow. Screenshots show scrolling of items in the LazyRow. Consider EventInCountry to be continuation of EventInCity (e.g. EventInCountry1 = EventInCity3 and EventInCountry2 = EventInCity4).
(screenshots are in reversed order)
I've created an issue to Google team: https://issuetracker.google.com/issues/343086052 to show that the first item in the LazyRow is not taken into account when it comes to LazyListState (IMHO, it should as it's part of the LazyRow)
s
I'm still not 100% sure I am understanding what the big is, but the UI tells me you're looking for a HorizontalPager and not a LazyRow here actually
Besides, in the last screenshot and the second to last screenshot, the first visible card looks to still be the one at index 0, since it's still peeking a little bit on the left there, it's definitely visible. So doing what you wanna do here is gonna be much harder than when just using a pager instead.
a
I see. Can I load items lazily like with LazyRow when using HorizontalPager?
s
Afaik yes
👍 1
thank you color 1
a
Thanks. That did the trick (still need to test lazy loading of multiple elements - seems 4 is not enough). I'm encountering one issue, though, and that is that there's no padding in between the cards (see screenshot). Have you seen this before?
s
You can probably play around with https://developer.android.com/develop/ui/compose/layouts/pager#content-padding and varying paddings inside the cards themselves to make it look as you want it to.
👍 1
thank you color 1