Animatable() in Jetpack Compose: A Guide to Smooth Animations

Mouad OumousMouad Oumous
3 min read

Jetpack Compose provides powerful animation APIs to create smooth and visually appealing UI effects. One such API is Animatable(), which allows you to animate values over time while giving you fine-grained control over their behavior.

In this article, we'll explore what Animatable() is, how it works, and how to use it effectively in your Jetpack Compose applications.

What is Animatable()?

Animatable() is a state-driven animation API in Jetpack Compose that helps you animate values such as floats, colors, and other numeric types. Unlike simple animate*AsState() functions, Animatable() allows more control over animations, such as pausing, resuming, stopping, or chaining animations.

Key Features:

  • Supports multiple data types (Float, Color, Dp, etc.)

  • Allows suspending functions for finer control (pause, resume, stop)

  • Provides precise control over animation duration, easing, and velocity

  • Enables sequential or parallel animations with coroutines

How to Use Animatable() in Jetpack Compose

To use Animatable(), follow these steps:

  1. Create an Animatable instance with an initial value.

  2. Use the animateTo() function to animate the value.

  3. Modify properties such as duration, easing, and velocity.

  4. Use coroutines to trigger and control animations.

Example 1: Basic Animation with Animatable()

Here's a simple example where we animate a circle's movement across the screen when a button is clicked:

@Composable
fun AnimatableExample() {
    val position = remember { Animatable(0f) }
    val scope = rememberCoroutineScope()

    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Box(
            modifier = Modifier
                .size(100.dp)
                .offset(x = position.value.dp)
                .background(Color.Blue, shape = CircleShape)
        )
        Spacer(modifier = Modifier.height(20.dp))
        Button(onClick = {
            scope.launch {
                position.animateTo(
                    targetValue = 300f,
                    animationSpec = tween(durationMillis = 1000, easing = LinearOutSlowInEasing)
                )
            }
        }) {
            Text("Move")
        }
    }
}

Explanation:

  • We create an Animatable(0f) instance to track the position.

  • A coroutine is launched when the button is clicked to animate the value from 0f to 300f.

  • We use tween() for smooth animation with easing.

Example 2: Animating Color with Animatable()

Animatable() is not limited to floats; it can also animate colors. Below is an example of changing the color of a box when clicked:

@Composable
fun ColorAnimationExample() {
    val color = remember { Animatable(Color.Red) }
    val scope = rememberCoroutineScope()

    Box(
        modifier = Modifier
            .size(200.dp)
            .background(color.value)
            .clickable {
                scope.launch {
                    color.animateTo(Color.Green, animationSpec = tween(1000))
                }
            }
    )
}

Explanation:

  • The box starts with Color.Red.

  • When clicked, it animates to Color.Green over 1 second using tween().

Example 3: Chaining Animations Sequentially

You can chain animations using coroutines to create more complex sequences:

@Composable
fun SequentialAnimationExample() {
    val size = remember { Animatable(100f) }
    val scope = rememberCoroutineScope()

    Box(
        modifier = Modifier
            .size(size.value.dp)
            .background(Color.Magenta)
            .clickable {
                scope.launch {
                    size.animateTo(200f, animationSpec = tween(500))
                    size.animateTo(100f, animationSpec = tween(500))
                }
            }
    )
}

Explanation:

  • The box grows to 200.dp and then shrinks back to 100.dp.

  • The animations run sequentially using coroutines.

Example 4: Spring Animation

To make an animation more natural, you can use spring() instead of tween():

scope.launch {
    position.animateTo(
        targetValue = 300f,
        animationSpec = spring(
            dampingRatio = Spring.DampingRatioMediumBouncy,
            stiffness = Spring.StiffnessLow
        )
    )
}

Explanation:

  • spring() makes the animation more dynamic, adding a bounce effect.

When to Use Animatable()?

  • When you need precise control over animation (pause, resume, stop)

  • When animating values other than simple floats (like Color or Dp)

  • When chaining animations or running them in sequence

  • When you want more control than animate*AsState() provides

Conclusion

Animatable() in Jetpack Compose is a powerful tool for creating smooth and controlled animations. Whether you're animating movement, color, or chaining multiple animations, Animatable() offers the flexibility you need to enhance your UI interactions.

By using coroutines and different animation specs (tween(), spring()), you can create engaging and dynamic animations effortlessly. Experiment with these examples to bring your Jetpack Compose UI to life!

0
Subscribe to my newsletter

Read articles from Mouad Oumous directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mouad Oumous
Mouad Oumous