DeferredTargetAnimation
@ExperimentalAnimatableApi
public class DeferredTargetAnimation<T, V : AnimationVector>(
private val vectorConverter: TwoWayConverter<T, V>
)
DeferredTargetAnimation
is intended for animations where the target is unknown at the time of
instantiation. Such use cases include, but are not limited to, size or position animations
created during composition or the initialization of a Modifier.Node, yet the target size or
position stays unknown until the later measure and placement phase.
DeferredTargetAnimation
offers a declarative updateTarget
function, which requires a target
to either set up the animation or update the animation, and to read the current value of the
animation.
Functions
public fun updateTarget(
target: T,
coroutineScope: CoroutineScope,
animationSpec: FiniteAnimationSpec<T> = spring(),
): T
updateTarget
sets up an animation, or updates an already running animation, based on the
target
in the given coroutineScope
. pendingTarget
will be updated to track the last
seen target
.
updateTarget
will return the current value of the animation after launching the animation
in the given coroutineScope
.
Returns
current value of the animation |
Code Examples
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)))
}
}