2

I have a scrollable LazyRow that contains numbers, I want for the middle visible number to be automatically selected as shown below:

enter image description here

How could I find the middle visible item in a LazyRow?

code:

@Composable
fun ScrollableAgeSelector(
    modifier: Modifier = Modifier
) {

    val numbers = (12..100).toList()

 
    LazyRow(
        modifier = modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.spacedBy(20.dp)
    ) {
        items(numbers.size) { index ->
            val num = numbers[index]
            NumberItem(
                num = num,
                selectedNum = 22
            )
        }
    }
}
Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
Roony
  • 132
  • 15
  • 1
    Look into `state.layoutInfo`, you can use that to know how many items are on the screen, therefore the middle item? https://stackoverflow.com/questions/72415419/how-to-know-items-which-are-fully-visible-in-the-view-port-of-a-lazy-row-in-jetp – Blundell Mar 28 '23 at 15:59

2 Answers2

4

You can calculate which item is in the center.

Something like:

    val state = rememberLazyListState()
    LazyRow(state = state) {
        itemsIndexed(itemsList) { index, item ->
            MyItem(
                state,
                index,
                //content = 
            )
        }
    }

With:

@Composable
fun MyItem(state: LazyListState, index: Int, /* content */) {

    val borderColor by remember {
        derivedStateOf {

            val layoutInfo = state.layoutInfo
            val visibleItemsInfo = layoutInfo.visibleItemsInfo
            val itemInfo = visibleItemsInfo.firstOrNull { it.index == index}

            itemInfo?.let {

                val delta = it.size/2 //use your custom logic
                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
            }
            Transparent
        }
    }

    Box(
        Modifier
            .padding(4.dp)
            .width(65.dp)
            .height(120.dp)
            .background(Yellow)
            .border(1.dp, borderColor),
        contentAlignment = Alignment.Center
    ){
        //content
    }

}

enter image description here

Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
  • Thank you, what if I want to add empty items before and after the LazyRow, what should I change to keep highliting the centred item? – Roony Mar 31 '23 at 18:51
1

Looks like you could use: LazyListLayoutInfo

https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListLayoutInfo#visibleItemsInfo()

val listState = rememberLazyListState()

    // Provide it to LazyColumn
    LazyColumn(state = listState) {
        // ...
    }

You can get the visibleItemsInfo from the listState. visibleItemsInfo is a List containing information about each visible item.

i.e. calling first() on the list would give you the index of the first visible item, and calling last() can get you the last visible item.

Then you get the item index off each of these items using index(): https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyListItemInfo#index()

You can use that information to know the positional index of the item in the middle.

Blundell
  • 75,855
  • 30
  • 208
  • 233
  • Thank you! Any idea how to keep scrolling after it reaches the end and the beginning? – Roony Mar 29 '23 at 07:39
  • If you know how many items need to be on screen for full width, you could add some blank items to the beginning and end? :-) – Blundell Mar 29 '23 at 08:12
  • I mean I want to wrap around when it reaches the beginning or the end, so if we have 0-50 you can scroll after 50 which is 0 and vice versa – Roony Mar 29 '23 at 08:22
  • something like: https://stackoverflow.com/questions/66503121/how-to-create-a-circular-endless-lazycolumn-lazyrow-in-compose – Blundell Mar 29 '23 at 08:31