Search code examples
androidandroid-jetpack-composeandroid-jetpack-compose-gesture

Android - How to listen to scroll in a Column modifier.verticalScroll


In Compose I am finding no way to listen to a list when it scrolls and react accordingly.

The main goal is to change a TopAppBar elevation according to how much the bottom list scrolls up or down; the bar elevation increases to 3 and decreases to 0 respectively, but so far I haven't even been able to listen to something as simple as the scroll of the content list below it.

This is how I have the content below the scaffold that contains the top bar:

// this is inside a composable
    val scrollState = rememberScrollState()

    Column(
        modifier = Modifier
            .fillMaxWidth()
            .wrapContentHeight()
            .padding(paddingValues)
            .verticalScroll(
                state = scrollState,
                enabled = true,
            )
    ) {

        // multiple list items here

    }

I cannot find any way to either listen to when scrollState changes, or when the Column scroll changes.

Available documentation shows nothing for this as far as I could find, nor could I find anything in SO either.

The only closest thing I found so far is strictly when using LazyColumn which I am not.

I could do this incredibly easily with kotlin + xml layouts, but when it comes to compose something that should be very simple like this feels impossible, how can this be done?


Solution

  • You can do it with NestedScrollConnection. You need to constrain bottom value to 0 and max value to maximum allowed by scrollState.

    @Preview
    @Composable
    fun NestedScrollSample() {
    
        var offset by remember {
            mutableStateOf(0f)
        }
    
        val scrollState = rememberScrollState()
        val nestedScrollConnection = remember {
            object : NestedScrollConnection {
                override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
                    offset = (offset - available.y).coerceIn(0f, scrollState.maxValue.toFloat())
                    return Offset.Zero
                }
            }
        }
    
        Column {
            Text(text = "Offset: $offset")
    
            Column(
                modifier = Modifier
                    .nestedScroll(nestedScrollConnection)
                    .fillMaxSize()
                    .verticalScroll(state = scrollState)
            ) {
                repeat(100) {
                    Text(
                        modifier = Modifier
                            .fillMaxWidth()
                            .background(Color.Green)
                            .padding(8.dp),
                        text = "Row $it",
                        color = Color.Red,
                        fontSize = 20.sp
                    )
                }
            }
        }
    }