Search code examples
androidkotlinanimationandroid-jetpack-composeandroid-transitions

Animate transition between column and row while scrolling in Jetpack Compose


When I scroll in a list, I want the composable sticky header to shrink. During that shrinking, the layout should by animated by the scrolling state to switch from a column layout, to a row layout.

Is there any way to achieve this in Jetpack Compose?

It should look like this: enter image description here


Solution

  • You can try use MotionLayout – it's like Constraint Layout but much better:

    implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1" 
    

    Firstly create a motion scene in json5 format. For your situation it will be something like this:

    {
      ConstraintSets: {
        start: {
          value: {
            start: [ 'parent', 'start' ],
            top: [ 'parent', 'top' ],
          },
          title: {
            start: [ 'parent', 'start' ],
            top: [ 'value', 'bottom' ],
          }
        },
        end: {
          value: {
            top: [ 'parent', 'top' ],
            start: [ 'title', 'end' ],
          },
          title: {
            start: [ 'parent', 'start' ],
            top: [ 'parent', 'top' ],
          }
        }
      }
    }
    

    MotionLayout gets 3 required parameters: motionScene (string with json5 above), content (your composable) and progress value (float from 0 to 1).

    1. Create scroll state and progress calculation:

      val scrollState = rememberScrollState() val progress = (scrollState.value.toFloat() / 100).takeIf { it <= 1 } ?: 1f

    2. Create MotionLayout:

       MotionLayout(
           motionScene = MotionScene(<json5 string>),
           progress = progress
       ) {
           Text(modifier = Modifier.layoutId("value"), text = "Value")
           Text(modifier = Modifier.layoutId("title"), text = "Title")
       }
      
    3. Connect the scrollState to your scrollable content:

       Column(modifier = Modifier.verticalScroll(scrollState)) {
           repeat(20) {
               Text(
                   modifier = Modifier
                       .fillMaxWidth()
                       .padding(16.dp),
                   text = it.toString()
               )
           }
       }
      

    the result of scrolling