Mobile Development 15 min read

Deep Dive into Compose Animatable API: Creation, animateTo, snapTo, and a Practical Upload Button Example

This article explains the low‑level Android Jetpack Compose Animatable API, its constructor parameters, how to create and configure Animatable instances for various value types, and demonstrates using animateTo and snapTo within coroutines, including listeners and a complete upload‑button use case.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Deep Dive into Compose Animatable API: Creation, animateTo, snapTo, and a Practical Upload Button Example

This article introduces the lower‑level animation API Animatable in Android Jetpack Compose, explains its constructor parameters, shows how to create and configure Animatable instances for different value types, and demonstrates the use of animateTo and snapTo methods, including coroutine handling and animation listeners.

Animatable

When we examined animateXxxAsState earlier, we discovered that it ultimately relies on Animatable . The Animatable class provides the core animation functionality, and higher‑level APIs such as animateValueAsState and animateXxxAsState are thin wrappers around it.

Constructor

The constructor requires three parameters:

initialValue : the starting value of the animation (e.g., Float, Dp, etc.).

typeConverter : a TwoWayConverter that converts between the value type and an AnimationVector . Compose supplies converters for common types such as Float, Int, Dp, Size, Color, etc.

visibilityThreshold : an optional threshold that, when reached, instantly finishes the animation.

class Animatable
(initialValue: T, val typeConverter: TwoWayConverter
, private val visibilityThreshold: T? = null)

Creating an Animatable

Example for a Float value:

val animatable = remember { Animatable(100f, Float.VectorConverter) }

For other types you pass the corresponding VectorConverter (Dp, Size, Color, etc.). Compose also provides convenience overloads for Float and Color where only the initial value is needed.

animateTo

The animateTo suspend function triggers the animation. Its signature is:

suspend fun animateTo(targetValue: T, animationSpec: AnimationSpec
= defaultSpringSpec, initialVelocity: T = velocity, block: (Animatable
.() -> Unit)? = null): AnimationResult

Parameters:

targetValue : the destination value.

animationSpec : configuration of the animation (spring, tween, etc.).

initialVelocity : starting velocity.

block : a callback executed on each frame, useful for listening to animation progress.

The function returns an AnimationResult containing the final AnimationState and an AnimationEndReason (either Finished or BoundReached ).

Running the animation

Because animateTo is a suspend function, it must be called from a coroutine. In Compose you can use LaunchedEffect or obtain a coroutine scope with rememberCoroutineScope() . Example using state‑driven animation:

var moveToRight by remember { mutableStateOf(false) }
val animatable = remember { Animatable(10.dp, Dp.VectorConverter) }

LaunchedEffect(moveToRight) {
    animatable.animateTo(if (moveToRight) 200.dp else 10.dp)
}
Box(
    Modifier
        .padding(start = animatable.value, top = 30.dp)
        .size(100.dp, 100.dp)
        .background(Color.Blue)
        .clickable { moveToRight = !moveToRight }
)

snapTo

The snapTo suspend function instantly sets the animation value to the target without any interpolation, equivalent to a zero‑duration SnapSpec :

suspend fun snapTo(targetValue: T)

It is also called from a coroutine, for example:

val animatable = remember { Animatable(10.dp, Dp.VectorConverter) }
val scope = rememberCoroutineScope()

Box(
    Modifier
        .padding(start = animatable.value, top = 30.dp)
        .size(100.dp, 100.dp)
        .background(Color.Blue)
        .clickable {
            scope.launch { animatable.snapTo(200.dp) }
        }
)

Practical Example – Upload Button

The article provides a complete composable that animates an upload button through four states (Normal, Start, Uploading, Success) using multiple Animatable instances for background color, text alpha, box width, progress, and progress alpha. The custom animateUploadAsState composable creates the required Animatable s, launches animateTo for each property when the state changes, and returns the interpolated UploadData object.

data class UploadData(
    val backgroundColor: Color,
    val textAlpha: Float,
    val boxWidth: Dp,
    val progress: Int,
    val progressAlpha: Float
)

@Composable
fun animateUploadAsState(value: UploadData, state: Any): UploadData {
    val bgColorAnimatable = remember { Animatable(value.backgroundColor, Color.VectorConverter(value.backgroundColor.colorSpace)) }
    val textAlphaAnimatable = remember { Animatable(value.textAlpha) }
    val boxWidthAnimatable = remember { Animatable(value.boxWidth, Dp.VectorConverter) }
    val progressAnimatable = remember { Animatable(value.progress, Int.VectorConverter) }
    val progressAlphaAnimatable = remember { Animatable(value.progressAlpha) }

    LaunchedEffect(state) { bgColorAnimatable.animateTo(value.backgroundColor) }
    LaunchedEffect(state) { textAlphaAnimatable.animateTo(value.textAlpha) }
    LaunchedEffect(state) { boxWidthAnimatable.animateTo(value.boxWidth) }
    LaunchedEffect(state) { progressAnimatable.animateTo(value.progress) }
    LaunchedEffect(state) { progressAlphaAnimatable.animateTo(value.progressAlpha) }

    return UploadData(
        bgColorAnimatable.value,
        textAlphaAnimatable.value,
        boxWidthAnimatable.value,
        progressAnimatable.value,
        progressAlphaAnimatable.value
    )
}

The UI composes a box whose size, color, and inner progress indicator animate according to the current UploadData . A secondary button toggles the upload state to demonstrate the animation flow.

Conclusion

The article demonstrates how to work directly with the low‑level Animatable API, covering creation, animateTo , snapTo , animation listeners, and result handling, and shows a real‑world upload‑button implementation that achieves the same effect as the higher‑level animateXxxAsState APIs.

MobileanimationuiAndroidKotlinComposeAnimatable
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.