How to Animate Content Changes in Jetpack Compose?

Animate Content Changes Jetpack Compose

In this article, we will learn how to animate content changes in Jetpack Compose using the AnimatedContent() and animateContentSize() composables.

Prerequisites:

AnimatedContent:

AnimatedContent composable animates the content changes. Currently, it is experimental.

Let’s understand it with an example.

@Composable
fun MyUI() {

    var count by remember {
        mutableStateOf(0)
    }

    Column(
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {

        Text(text = "Count: $count")

        Button(
            modifier = Modifier.padding(top = 8.dp),
            onClick = {
                count++
            }
        ) {
            Text(text = "Increase Count")
        }
    }
}

Output:

animate text

Let’s animate the count value. Wrap the Text() inside the AnimatedContent() composable.

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun MyUI() {

    var count by remember {
        mutableStateOf(0)
    }

    Column(
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {

        AnimatedContent(targetState = count) { targetState ->
            Text(text = "Count: $targetState")
        }
        
        Button(
            modifier = Modifier.padding(top = 8.dp),
            onClick = {
                count++
            }
        ) {
            Text(text = "Increase Count")
        }
    }
}

Output:

animate content text

Let’s look at the AnimatedContent() composable.

@ExperimentalAnimationApi
@Composable
fun <S> AnimatedContent(
    targetState: S,
    modifier: Modifier = Modifier,
    transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = {
        fadeIn(animationSpec = tween(220, delayMillis = 90)) +
            scaleIn(initialScale = 0.92f, animationSpec = tween(220, delayMillis = 90)) with
            fadeOut(animationSpec = tween(90))
    },
    contentAlignment: Alignment = Alignment.TopStart,
    content: @Composable() AnimatedVisibilityScope.(targetState: S) -> Unit
)

targetState – When this value is changed, the AnimatedContent() animates its content.

modifierThe modifier for changing the layout.

transitionSpecTo customize the animations.

contentAlignment – The position of the content.

content – The content that gets animated.

Let’s add custom animations. The current value should fade out and the new value should fade in. We can implement it like this:

AnimatedContent(
    targetState = count,
    transitionSpec = {
        fadeIn() with fadeOut()
    }
) { targetCount ->
    Text(
        text = "$targetCount",
        fontSize = 20.sp
    )
}

Output:

text fade in fade out

In the above code, the fadeIn() and fadeOut() are used for the enter and exit transitions respectively. Both of them are combined using the with infix function.

See all the transition APIs in Jetpack Compose.

Jetpack Compose Animate Content Changes for Child Components:

We can specify the animations for the children using the animateEnterExit() modifier.

AnimatedContent(
    targetState = count,
    transitionSpec = {
        EnterTransition.None with ExitTransition.None
    }
) { targetCount ->
    Text(
        modifier = Modifier.animateEnterExit(
            enter = scaleIn(),
            exit = scaleOut()
        ),
        text = "$targetCount",
        fontSize = 20.sp
    )
}

Output:

jetpack compose scale in out

Animate Content Size:

We can also animate the content changes using the animateContentSize() modifier.

@Composable
fun MyUI() {

    val smallText = "Hello"
    val largeText = "Hello Android"

    var text by remember {
        mutableStateOf(smallText)
    }

    Column(
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {

        Text(
            modifier = Modifier
                .animateContentSize()
                .background(color = Color.Yellow.copy(alpha = 0.2f))
                .padding(horizontal = 8.dp, vertical = 4.dp)
                .clickable {
                    text = if (text == smallText) {
                        largeText
                    } else {
                        smallText
                    }
                },
            text = text,
            fontSize = 20.sp
        )
    }
}

Output:

text expand shrink

Note: The order of modifiers matters in Jetpack Compose. So place the animateContentSize() before any size modifiers (such as size or defaultMinSize) to get the expected animation effects.

Let’s look at the animateContentSize():

fun Modifier.animateContentSize(
    animationSpec: FiniteAnimationSpec<IntSize> = spring(),
    finishedListener: ((initialValue: IntSize, targetValue: IntSize) -> Unit)? = null
)

animationSpecAnimation specification.

finishedListener – It is called when the animation is completed.

Custom animateContentSize():

Let’s change the animationSpec to tween().

Text(
    modifier = Modifier
        .animateContentSize(
            animationSpec = tween(durationMillis = 200)
        )
        .background(color = Color.Yellow.copy(alpha = 0.2f))
        .padding(horizontal = 8.dp, vertical = 4.dp)
        .clickable {
            text = if (text == smallText) {
                largeText
            } else {
                smallText
            }
        },
    text = text,
    fontSize = 20.sp
)

Output:

jetpack compose text tween

This is all about animating content changes in Android Jetpack Compose. I hope you learned something new. If you have any doubts, comment below.

Leave a Comment