Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting centered item inside LazyRow in Jetpack Compose

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.

enter image description here

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

}
like image 742
Alaa AbuZarifa Avatar asked Dec 06 '25 15:12

Alaa AbuZarifa


1 Answers

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
    )

enter image description here

like image 171
Gabriele Mariotti Avatar answered Dec 08 '25 10:12

Gabriele Mariotti



Donate For Us

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