Invoke [onPlaced] after the parent [LayoutModifier] and parent layout has been placed and before child [LayoutModifier] is placed.
OnPlaced
@Composable
fun OnPlaced() {
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 {
// 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 = 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
}
}
@Composable
fun AnimatedChildAlignment(alignment: Alignment) {
Box(Modifier.fillMaxSize().padding(4.dp).border(1.dp, Color.Red)) {
Box(
modifier =
Modifier.animatePlacement().align(alignment).size(100.dp).background(Color.Red)
)
}
}
}