Getting Started with Animations in Jetpack Compose

Kadir Kuruca
5 min readNov 14, 2024

--

Introduction

Jetpack Compose makes animations easier and more fun. With just a few lines of code, you can add smooth effects that make your app feel more interactive and professional. In this article, I’ll show you some simple yet powerful animations you can create in Jetpack Compose. These examples are perfect for basic animations, so let’s dive in!

1. Expanding and Shrinking a Card with animateDpAsState

Let’s start with an animation that makes a card grow and shrink when you tap on it. This effect is simple to add but gives a nice touch to your UI.

@Composable
fun AnimateDpAsStateExample(modifier: Modifier = Modifier) {
var isExpanded by remember { mutableStateOf(false) }
val size by animateDpAsState(targetValue = if (isExpanded) 300.dp else 150.dp)

Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Card(
modifier = Modifier
.size(size)
.clickable {
isExpanded = !isExpanded
},
colors = CardDefaults.cardColors(
containerColor = Color.DarkGray
)
) {
Image(
painter = painterResource(id = R.drawable.ic_android),
contentDescription = "android_icon",
contentScale = ContentScale.Fit,
modifier = Modifier.size(size)
)
}
}
}

In this code, the card will change size each time you tap it.

Output:

2. Rotating an Image with animateFloatAsState

Want a rotation effect? This example rotates the image 180 degrees when you tap it. The animateFloatAsState function makes this super easy!

@Composable
fun AnimateFloatAsStateExample(modifier: Modifier = Modifier) {
var isRotating by remember { mutableStateOf(false) }
val rotation by animateFloatAsState(if (isRotating) 180f else 0f)

Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Card(
modifier = Modifier
.size(200.dp)
.clickable { isRotating = !isRotating },
colors = CardDefaults.cardColors(containerColor = Color.DarkGray)
) {
Image(
painter = painterResource(id = R.drawable.ic_android),
contentDescription = "android_icon",
contentScale = ContentScale.Fit,
modifier = Modifier.rotate(rotation)
)
}
}
}

In this code, the image will rotate 180 degrees each time you tap it.

Output:

3. Coordinated Animations with updateTransition

Sometimes, you want multiple animations to happen together. Here’s an example that combines color and size changes using updateTransition.

@Composable
fun TransitionAnimation(modifier: Modifier = Modifier) {
var isClicked by remember { mutableStateOf(false) }
val transition = updateTransition(targetState = isClicked, label = "Transition")
val color by transition.animateColor(label = "Color Transition") { state ->
if (state) Color.Gray else Color.Black
}
val size by transition.animateDp(label = "Size Transition") { state ->
if (state) 300.dp else 150.dp
}

Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Card(
modifier = Modifier
.size(size)
.clickable { isClicked = !isClicked },
colors = CardDefaults.cardColors(containerColor = color)
) {
Image(
painter = painterResource(id = R.drawable.ic_android),
contentDescription = "android_icon",
contentScale = ContentScale.Fit,
modifier = Modifier.size(size)
)
}
}
}

Now, both the color and size change with each tap, making the animation feel even more engaging.

Output:

4. Creating Keyframe Animation with animateDpAsState

For a more advanced animation, you can set up keyframes to create specific points in the animation.

In Jetpack Compose, keyframe animations allow us to control exactly how and when changes occur over time. This technique lets us break down an animation into several points or “keyframes,” so we can add unique motions within the same animation cycle. For instance, we can make a component grow to a specific size, pause there briefly, then shrink back, all in one fluid animation.

This example makes the card grow and shrink at different speeds, giving it a unique motion.

@Composable
fun KeyframeAnimation(modifier: Modifier = Modifier) {
var isClicked by remember { mutableStateOf(false) }
val size by animateDpAsState(
targetValue = if (isClicked) 200.dp else 100.dp,
animationSpec = keyframes {
durationMillis = 1000 // Total duration of the animation
50.dp at 300 with LinearOutSlowInEasing
150.dp at 700 with FastOutLinearInEasing
}
)

Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Card(
modifier = Modifier
.size(size)
.clickable { isClicked = !isClicked },
colors = CardDefaults.cardColors(containerColor = Color.DarkGray)
) {
Image(
painter = painterResource(id = R.drawable.ic_android),
contentDescription = "android_icon",
contentScale = ContentScale.Fit,
modifier = Modifier.size(size)
)
}
}
}

Let’s look more closely at each part:

  • durationMillis = 1000: This sets the total time for the animation to complete, in milliseconds. Here, it’s set to 1000 ms (or 1 second). The animation will complete in this time frame.
  • 50.dp at 300 with LinearOutSlowInEasing: This line tells the animation to reach a size of 50.dp after 300 ms from the start. The easing function LinearOutSlowInEasing makes the size change start quickly and then slow down toward the 300 ms mark.
  • 150.dp at 700 with FastOutLinearInEasing: This line directs the animation to expand to 150.dp at 700 ms. The FastOutLinearInEasing function means it starts out quickly and then smooths into a more even pace.

Output:

5. Looping Animation with rememberInfiniteTransition

For animations that keep looping (like loading animations), you can use rememberInfiniteTransition. Here’s an example that makes a box smoothly shift colors forever.

@Composable
fun InfiniteTransitionAnimation(modifier: Modifier = Modifier) {
val infiniteTransition = rememberInfiniteTransition(label = "Infinite Transition")
val color by infiniteTransition.animateColor(
initialValue = Color(0xFFec4913), // Red
targetValue = Color(0xFF1380ec), // Blue
animationSpec = infiniteRepeatable(
animation = tween(1500),
repeatMode = RepeatMode.Reverse
)
)

Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Card(
modifier = Modifier.size(200.dp),
colors = CardDefaults.cardColors(containerColor = color)
) {
Image(
painter = painterResource(id = R.drawable.ic_android),
contentDescription = "android_icon",
contentScale = ContentScale.Fit,
colorFilter = ColorFilter.tint(Color.White),
modifier = Modifier.size(200.dp)
)
}
}
}

This creates a simple color-changing animation that repeats continuously, adding life to static screens.

Output:

Wrap-Up

Jetpack Compose makes it easy to add fun and engaging animations to your Android app. From simple expanding shapes to looping colors, these examples will help you bring your app to life. Try these out, experiment with them, and see what you can create!

Happy coding!

--

--

No responses yet