I want to make a horizontal list with a carousel-like effect, the middle item must be positioned at the center of the screen, with the previous and next items showing only their edges. Additionally, the centered item should be slightly scaled up to emphasize its central position, see the gif below.

I also want to change the background color when the item is at the center, but it's not working for me, my attempt below kinda works, sometimes it's slow and sometimes it just doesn't work.
Can you please enhance it so the centered item automatically gets updated and scaled?
My Code:
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MyCards(list: List<String>) {
val state = rememberLazyListState()
val snappingLayout = remember(state) { SnapLayoutInfoProvider(state) }
val flingBehavior = rememberSnapFlingBehavior(snappingLayout)
LazyRow(
modifier = Modifier.wrapContentHeight(),
verticalAlignment = Alignment.CenterVertically,
state = state,
flingBehavior = flingBehavior
) {
items(list) {
MyCard(state,list.indexOf(it))
}
}
}
@Composable
private fun MyCard(state: LazyListState, index: Int) {
val isCentered = remember { mutableStateOf(false) }
val itemInfo = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == index}
itemInfo?.let {
val center = state.layoutInfo.viewportEndOffset / 2
val childCenter = it.offset + it.size / 2
val target = childCenter - center
if (index == target) isCentered.value = true
}
Card(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(10.dp, 10.dp, 10.dp, 200.dp),
elevation = 5.dp,
shape = RoundedCornerShape(10.dp),
backgroundColor = if (isCentered.value) Color.Red else Color.Blue
) {
Image(
modifier = Modifier
.padding(70.dp)
.height(400.dp)
.width(100.dp)
.alpha(0f),
painter = painterResource(id = R.drawable.ico_main),
contentDescription = null
)
}
}
In you Card you can use something like:
val backgroundColor by remember {
derivedStateOf {
val layoutInfo = state.layoutInfo
val visibleItemsInfo = layoutInfo.visibleItemsInfo
val itemInfo = visibleItemsInfo.firstOrNull { it.index == index}
itemInfo?.let {
//First item
if (itemInfo.index == 0 && itemInfo.offset == 0)
return@derivedStateOf Red
//Last item
val viewportWidth = layoutInfo.viewportEndOffset + layoutInfo.viewportStartOffset
if (itemInfo.index + 1 == layoutInfo.totalItemsCount &&
itemInfo.offset + itemInfo.size <= viewportWidth)
return@derivedStateOf Red
//Other items
val delta = 5
val center = state.layoutInfo.viewportEndOffset / 2
val childCenter = it.offset + it.size / 2
val target = childCenter - center
if (target in -delta..delta) return@derivedStateOf Red
}
Blue
}
}
val scale by remember {
derivedStateOf {
val currentItem = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == index } ?: return@derivedStateOf 1.0f
val halfRowWidth = state.layoutInfo.viewportSize.width/2
(1f - minOf(1f, abs(currentItem.offset + (currentItem.size / 2) - halfRowWidth ).toFloat() / halfRowWidth) * 0.10f)
}
}
and then apply it
Card(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(10.dp, 10.dp, 10.dp, 200.dp)
.scale(scale)
.zIndex(scale * 10),
//
backgroundColor = backgroundColor
)

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With