[DeferredTargetAnimation] is intended for animations where the target is unknown at the time of instantiation.
DeferredTargetAnimationSample
@OptIn(ExperimentalAnimatableApi::class)
@Composable
fun DeferredTargetAnimationSample() {
// Creates a custom modifier that animates the constraints and measures child with the
// animated constraints. This modifier is built on top of `Modifier.approachLayout` to approach
// th destination size determined by the lookahead pass. A resize animation will be kicked off
// whenever the lookahead size changes, to animate children from current size to destination
// size. Fixed constraints created based on the animation value will be used to measure
// child, so the child layout gradually changes its animated constraints until the approach
// completes.
fun Modifier.animateConstraints(
sizeAnimation: DeferredTargetAnimation<IntSize, AnimationVector2D>,
coroutineScope: CoroutineScope,
) =
this.approachLayout(
isMeasurementApproachInProgress = { lookaheadSize ->
// Update the target of the size animation.
sizeAnimation.updateTarget(lookaheadSize, coroutineScope)
// Return true if the size animation has pending target change or is currently
// running.
!sizeAnimation.isIdle
}
) { measurable, _ ->
// In the measurement approach, the goal is to gradually reach the destination size
// (i.e. lookahead size). To achieve that, we use an animation to track the current
// size, and animate to the destination size whenever it changes. Once the animation
// finishes, the approach is complete.
// First, update the target of the animation, and read the current animated size.
val (width, height) = sizeAnimation.updateTarget(lookaheadSize, coroutineScope)
// Then create fixed size constraints using the animated size
val animatedConstraints = Constraints.fixed(width, height)
// Measure child with animated constraints.
val placeable = measurable.measure(animatedConstraints)
layout(placeable.width, placeable.height) { placeable.place(0, 0) }
}
var fullWidth by remember { mutableStateOf(false) }
// Creates a size animation with a target unknown at the time of instantiation.
val sizeAnimation = remember { DeferredTargetAnimation(IntSize.VectorConverter) }
val coroutineScope = rememberCoroutineScope()
Row(
(if (fullWidth) Modifier.fillMaxWidth() else Modifier.width(100.dp))
.height(200.dp)
// Use the custom modifier created above to animate the constraints passed
// to the child, and therefore resize children in an animation.
.animateConstraints(sizeAnimation, coroutineScope)
.clickable { fullWidth = !fullWidth }
) {
Box(Modifier.weight(1f).fillMaxHeight().background(Color(0xffff6f69)))
Box(Modifier.weight(2f).fillMaxHeight().background(Color(0xffffcc5c)))
}
}