Reputation: 73
I built a complex lazyColumn composable that mimic a card (indeed, I need to have a list of cards containing potentialy many items each, and nesting a LazyColumn in a Card is not possible (due to the "Vertically scrollable component was measured with an infinity maximum height contraints" issue). So my composable gets a list of data items of this kind :
data class CardsState(val cards: ImmutableList<Card>)
data class Card(
val header: String,
val items: ImmutableList<CardItem>,
)
In the LazyColumn, it can takes several LazyListScope.item to draw one CardItem.
So far so good.
But I also need sometimes to scroll to a specific CardItem. Unfortunately, I can't use the LazyListState.animateScrollToItem(index: Int) function as it takes the item index as parameter, and i can't tell what is the lazyColumn item index I need as it can be tricky to calculate.
The best for me would be to have a LazyListState.animateScrollToItem(key: Any) as all my LazyColumn items have a unique key that I can pass as a parameter to my state like this :
interface CollectionItem {
val key: Any?
}
fun CollectionItem.makeKey(): String = buildString {
append(javaClass.name)
key?.let { append("_$it") }
}
data class CardsState(
val cards: ImmutableList<Card>,
val scrollToItemWithKey: Any?, // The key of the lazyColumn item I want to scroll to
)
data class Card(
val header: String,
val items: ImmutableList<CardItem>,
) : CollectionItem {
override val key: String = header
}
sealed class CardItem : CollectionItem {
data class CardItemTypeA(/* some parameters */) : CardItem()
data class CardItemTypeB(/* some others*/) : CardItem()
}
@Composable
fun MyContent(cardsState: CardsState) {
val scrollState = rememberLazyListState()
LazyColumn(state = scrollState) {
cardsState.cards.forEach { card ->
item(key = "${card.makeKey()}_top") { Spacer(modifier = Modifier.height(16.dp)) }
item(key = "${card.makeKey()}_header") { Header(card.header) }
items(items = card.items, key = { it.makeKey() }) {
when (it) {
is CardItem.CardItemTypeA -> ComposeCardItemTypeA()
is CardItem.CardItemTypeB -> ComposeCardItemTypeB()
}
item(key = "${card.makeKey()}_bottom") { CardBottomLayout() }
}
}
}
}
But obviously this scrollToItemWithKey doesn't exist. Of course I could "calculate" the lazycolumnIndex knowing how it is built, but that makes my calculation made in a ViewModel dependent on the way the view is built on the compose side, and can be tricky, especially if I want to add other composable within my main LazyColumn. Is there another way for me to achieve this scrolling ?
Upvotes: 1
Views: 205
Reputation: 15579
Of course I could "calculate" the lazycolumnIndex knowing how it is built, but that makes my calculation made in a ViewModel dependent on the way the view is built on the compose side
That calculation doesn't belong in the view model, it should be done somewhere near/in the LazyColumn because it is dependent on how that is constructed. Either create a mapping of CardItem to its index in the final LazyColumn and make sure it is only recalculated when cardsState.cards
changes (like, remember(cardsState.cards) { ... }
and use this mapping to retrieve the precalculated index when you need to scroll, or calculate the index on demand when you want to execute the scroll.
Upvotes: 0