Animation in Jetpack Compose

July 1, 2022

This post is meant to be an alternative and a complementary way of presenting information found in https://developer.android.com/jetpack/compose/animation. Laying it out this way helped me make more sense of that documentation and I hope it will help you as well.

First, this post provides a high-level overview of the Animation APIs in Jetpack Compose and then outlined more detail about concrete APIs used in each scenario.

No code snippets here—this post is meant to provide foundational overview of Compose Animation APIs to help you choose most suitable API depending on your animation needs.

Which API to use?

Here is a decision-making diagram to help you chose the right Animation API depending on your needs. More detail about each is provided below.

If animating content change in layout (composition)

  1. If animating appearance and disappearance

  2. If swapping content based on state

  3. Otherwise, use Modifier.animateContentSize

If the animation is state-based and happens during composition

  1. If animation is infinite

  2. If not infinite and animating multiple values simultaneously

  3. Otherwise, use animate*AsState

If need fine control over animation time

  • Use Animation, such as TargetBasedAnimation or DecayAnimation

If animation the only source of truth

Otherwise, use AnimationState or animate

Animation APIs in Compose

  • Brand new APIs written for Jetpack Compose

  • Many APIs are declarative—you can write animations in a declarative manner.

  • State-based APIs are interruptible—when an animation is interrupted by another one, the values from the ongoing animation can be carried over to the new one.

  • Many animations have reasonable defaults (via default function parameters) and are highly customizable with help of AnimationSpec and AnimationVector.

  • Many APIs are built as composable functions on top of lower-level APIs built with coroutine suspend functions.

  • Android Studio includes powerful tooling support to help you implement and debug animations.

AnimatedVisibility

  • Composable

  • Used to animate the appearance and disappearance of its content

  • Can be customized by EnterTransition and ExitTransition

  • Use Modifier.animateEnterExit to specify different animation behavior for each direct and indirect child.

  • If only children need to be animated (by using Modifier.animateEnterExit), set EnterTransition.None and ExitTransition.None on the parent AnimatedVisibility composable.

  • AnimatedVisibility can be constructed with MutableTransitionState which allows triggering this animation as soon as the AnimatedVisibility is added to the composition tree. It is also useful for observing the animation state.

Crossfade

  • Composable

  • Animates between two layouts (composables) with a crossfade animation.

  • Crossfade expects some kind of state to detect when it should recompose.

  • Use animationSpec to customize crossfade animation.

AnimatedContent

  • Composable

  • Still experimental as of Compose 1.2.0

  • Animates its content as it changes based on a target state

  • Use transitionSpec parameter to customize animation behavior

Modifier.animateContentSize

  • Animates its own size when its child modifier (or the child composable if it is already at the tail of the chain) changes size. This allows the parent modifier to observe a smooth size change.

rememberInfiniteTransition

  • Composable

  • Creates a InfiniteTransition that runs infinite child animations.

  • Child animations can be added using:

    • animateColor

    • animateFloat

    • animateValue

  • Child animations will start running as soon as they enter the composition, and will not stop until they are removed from the composition.

  • Specify an infiniteRepeatable to provide the animation specifications.

updateTransition

  • Composable

  • Used to apply multiple animations at the same time.

  • A Transition is created by calling updateTransition(targetState)

  • Animate between initialState and targetState using below extension functions on a Transition:

    • animateColor

    • animateFloat

    • animateDp

    • animateValue

    • animateInt

    • animateIntOffset

    • animateIntSize

    • animateOffset

    • animateRect

    • animateSize

  • Specify transitionSpec parameter to pass a different AnimationSpec for each transition state change.

  • Use isTransitioningTo infix function in transitionSpec to determine the direction of the state change.

animate*AsState

  • Composable

  • Used for animating a single value. You only need to provide the target value

  • During the animation, animate*AsState composable gets recomposed and returns an updated animation State for every frame.

  • Out of the box, Compose comes with animate*AsState for:

    • Float

    • Color

    • Dp

    • Size

    • Offset

    • Rect

    • Int

    • IntOffset

    • IntSize

  • Add support for other data types by providing a TwoWayConverter to animateValueAsState

  • Customize the animation by providing an AnimationSpec

  • Once running, we can’t stop/cancel this animation until it’s removed from the composition tree

  • Under the hood, lower level API Animatable is used by animate*AsState

Animatable

  • Not a composable. Can be used outside of a composition

  • Coroutine-based API

  • Single Source of Truth (ensures consistent continuation and mutual exclusiveness). The value change is always continuous and any ongoing animation can be canceled and the new animation will start from the current snapshot value with the current velocity.

  • Animatable state can be updated using animateTo, snapTo and animateDecay commonly launched in a LaunchedEffect

  • snapTo sets the current value to the target value immediately. This is useful when the animation itself is not the only source of truth and has to be synced with other states, such as touch events

  • animateDecay starts an animation that slows down from the given velocity. This is useful for implementing fling behavior

  • Out of the box, supports Float and Color, but any data type can be used by providing a TwoWayConverter.

  • Unlike animate*AsState, can have an initial value different from its first target value

  • Customizable via AnimationSpec

  • Animatable has AnimationState

animate and AnimationState

  • Not a composable. Can be used outside of a composition

  • Animates from the given initialValue towards the targetValue with optional initialVelocity. Use animationSpec to customize animation behavior

  • One shot animation: Unlike Animatable, does not provide consistent continuation and mutual exclusiveness

  • animate is a suspend function

  • Execute multiple animations in the same coroutine to achieve sequential (chained) animations. In the view system, we had to use animation end listeners for this

  • Execute animations in separate coroutines to achieve simultaneous (parallel) animations

  • If there's a need to access more info related to the animation such as start time, target, etc, consider using AnimationState.animateTo

Further Reading

  1. Animation in Jetpack Compose

  2. Codelab: Animating elements in Jetpack Compose

  3. Video: Compose animations reimagined

Previous
Previous

Basic Auth with OkHttp and Retrofit

Next
Next

Compose sample app: UI state with Flow, offline first