androidkotlinanimationandroid-jetpack-composejetpack-compose-animation

Animate Column from Arrangement.Center to Arrangement.Top


I am using Jetpack Compose native animations to create a complex transition with some steps. The issue is that the original Column has the arrangement set to Center and after the inclusion of some elements in the column, the arrangement needs to switch to Top. If I just change the arrangement, I would like a continuous effect instead of the current break.

Is there any way to animate this kind of property?

Regards.


Solution

  • It can be done with Modifier.onPlaced and Modifier.offset by creating a Modifier

    fun Modifier.animatePlacement(): Modifier = composed {
        val scope = rememberCoroutineScope()
        var targetOffset by remember { mutableStateOf(IntOffset.Zero) }
        var animatable by remember {
            mutableStateOf<Animatable<IntOffset, AnimationVector2D>?>(null)
        }
        this
            // 🔥 onPlaced should be before offset Modifier
            .onPlaced {
                // Calculate the position in the parent layout
                targetOffset = it
                    .positionInParent()
                    .round()
            }
            .offset {
                // Animate to the new target offset when alignment changes.
                val anim = animatable ?: Animatable(targetOffset, IntOffset.VectorConverter)
                    .also {
                        animatable = it
                    }
    
    
                if (anim.targetValue != targetOffset) {
                    scope.launch {
                        anim.animateTo(targetOffset, spring(stiffness = Spring.StiffnessMediumLow))
                    }
                }
                // Offset the child in the opposite direction to the targetOffset, and slowly catch
                // up to zero offset via an animation to achieve an overall animated movement.
                animatable?.let { it.value - targetOffset } ?: IntOffset.Zero
            }
    }
    

    Some Composable that this modifier is assigned to

    @Composable
    fun AnimatedChildArrangement(arrangement: Arrangement.Vertical) {
        Column(
            Modifier
                .fillMaxSize()
                .padding(4.dp)
                .border(2.dp, Color.Green),
            verticalArrangement = arrangement
        ) {
            Box(
                modifier = Modifier
                    .animatePlacement()
                    .size(100.dp)
                    .background(Color.Red)
            )
        }
    }
    

    Demo

    @Preview
    @Composable
    private fun Test() {
    
        var arrangement: Arrangement.Vertical by remember {
            mutableStateOf(Arrangement.Center)
        }
    
        Column {
            Button(onClick = {
                arrangement = if (arrangement == Arrangement.Center) {
                    Arrangement.Top
                } else {
                    Arrangement.Center
    
                }
            }) {
                Text(text = "arrangement: $arrangement")
            }
    
            AnimatedChildArrangement(arrangement = arrangement)
        }
    }