
In this article, we’ll learn how to implement different text animations in Jetpack Compose.
Prerequisites:
For this article, create an empty Jetpack Compose project and open MainActivity.kt. Create a MyUI() composable and call it from the onCreate() method. We’ll write our code in it.
MainActivity for Material 3 Jetpack Compose:
// add the following packages
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.with
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YourProjectNameTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
MyUI()
}
}
}
}
}
}
@Composable
fun MyUI() {
}
For the Material 2 version:
// add the following packages
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.with
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YourProjectNameTheme(darkTheme = false) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
MyUI()
}
}
}
}
}
}
@Composable
fun MyUI() {
}
Note: I am using the Material 3 Jetpack Compose. If you are working with Material 2, you may see a slightly different appearance, but the code and animation will still work as expected.
First, let’s create a text and button.
@Composable
fun MyUI() {
var count by remember {
mutableStateOf(0)
}
Text(
text = "$count",
fontSize = 36.sp
)
// gap between text and button
Spacer(modifier = Modifier.height(height = 12.dp))
Button(onClick = { count++ }) {
Text(text = "Increment")
}
}
Output:

We have added a count variable. It will be incremented by one every time the button is tapped. The current value of the count is displayed using the Text() composable. Let’s add some animations.
Related: Margin and Padding Using Spacer()
1. Slide-Up Animation:
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun MyUI() {
var count by remember {
mutableStateOf(0)
}
AnimatedContent(
targetState = count,
transitionSpec = {
slideIntoContainer(
towards = AnimatedContentScope.SlideDirection.Up,
animationSpec = tween(durationMillis = 500)
) with ExitTransition.None
}
) { targetCount ->
Text(
text = "$targetCount",
fontSize = 36.sp
)
}
Spacer(modifier = Modifier.height(height = 12.dp))
Button(onClick = { count++ }) {
Text(text = "Increment")
}
}
Output:

To animate the text, we put it inside the AnimatedContent composable. We passed the count to the targetState parameter. Every time the parameter gets a new value, the text in the AnimatedContent will be animated.
Inside the AnimatedContent lambda, we should use the targetCount variable, not the state variable (count), as this will ensure the animation runs smoothly.
We added enter and exit animations using the transitionSpec parameter. The enter animation slides the text into the container from the bottom. There is no exit animation.
Related: Button Styles in Jetpack Compose
2. Slide Down Animation:
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun MyUI() {
var count by remember {
mutableStateOf(0)
}
AnimatedContent(
targetState = count,
transitionSpec = {
slideIntoContainer(
towards = AnimatedContentScope.SlideDirection.Down,
animationSpec = tween(durationMillis = 500)
) with ExitTransition.None
}
) { targetCount ->
Text(
text = "$targetCount",
fontSize = 36.sp
)
}
Spacer(modifier = Modifier.height(height = 12.dp))
Button(onClick = { count++ }) {
Text(text = "Increment")
}
}
Output:

We just changed the towards parameter to Down. The text is entering the container from the top.
3. Slide In and Out Animation:
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun MyUI() {
var count by remember {
mutableStateOf(0)
}
AnimatedContent(
targetState = count,
transitionSpec = {
slideIntoContainer(
towards = AnimatedContentScope.SlideDirection.Up,
animationSpec = tween(durationMillis = 500)
) with
slideOutOfContainer(
towards = AnimatedContentScope.SlideDirection.Up,
animationSpec = tween(durationMillis = 500)
)
},
contentAlignment = Alignment.Center
) { targetCount ->
Text(
text = "$targetCount",
fontSize = 36.sp
)
}
Spacer(modifier = Modifier.height(height = 12.dp))
Button(onClick = { count++ }) {
Text(text = "Increment")
}
}
Output:

We added the exit animation using the slideOutOfContainer() method. Don’t forget to set the contentAlignment to Center. Otherwise, you get a problem when the count value is changed from 9 to 10.
4. Fade Animation:
Jetpack Compose provides fadeIn() and fadeOut() methods.
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun MyUI() {
var count by remember {
mutableStateOf(0)
}
AnimatedContent(
targetState = count,
transitionSpec = {
fadeIn(animationSpec = tween(durationMillis = 500)) with
fadeOut(animationSpec = tween(durationMillis = 500))
},
contentAlignment = Alignment.Center
) { targetCount ->
Text(
text = "$targetCount",
fontSize = 36.sp
)
}
Spacer(modifier = Modifier.height(height = 12.dp))
Button(onClick = { count++ }) {
Text(text = "Increment")
}
}
Output:

Related: AnimatedVisibility in Jetpack Compose
5. Scale Animation:
We can animate the text size using the scaleIn() method.
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun MyUI() {
var count by remember {
mutableStateOf(0)
}
AnimatedContent(
targetState = count,
transitionSpec = {
scaleIn(animationSpec = tween(durationMillis = 500)) with
ExitTransition.None
},
contentAlignment = Alignment.Center
) { targetCount ->
Text(
text = "$targetCount",
fontSize = 36.sp
)
}
Spacer(modifier = Modifier.height(height = 12.dp))
Button(onClick = { count++ }) {
Text(text = "Increment")
}
}
Output:

This is all about text animations in Jetpack Compose. I hope you have learned something new. If you have any doubts, leave a comment below.
Related Articles: