Learning Android Development
Unlocking the Magic of Dynamic Composable Animations in Jetpack Compose
Elevate Your UI with Animated Visibility and Launched Effect in Compose
Have you ever been captivated by the smooth, eye-catching animations in modern apps? From impressive entrance effects to graceful fades, animations can truly elevate the user experience. With Jetpack Compose, Google’s declarative UI toolkit for Android, creating these stunning animations has never been easier.
In this article, we’ll explore how to create two popular animation types — Scale Animation and Fade Animation — using Compose. We’ll make it simpler to add some values present by default. Finally, we’ll extend our knowledge to include SlideIn
and SlideOut
animations for an all-encompassing animated experience.
Scale Animation — Magnify / Minify Your Elements
Scaling elements is an excellent way to draw attention to specific parts of your UI. Let’s dive into the ScaleAnimation
function.
@Composable
fun ScaleAnimation(
visible: Boolean,
startScale: Float = 0.8f,
endScale: Float = 1.0f,
animationDuration: Int = 300,
content: @Composable () -> Unit,
) {
AnimatedVisibility(
visible = visible,
enter = scaleIn(
initialScale = startScale,
animationSpec = tween(durationMillis = animationDuration)
),
exit = scaleOut(
targetScale = endScale,
animationSpec = tween(durationMillis = animationDuration)
)
) {
content()
}
}
Usage and Examples
By default, ScaleAnimation
will smoothly scale the content from startScale
to endScale
within animationDuration
milliseconds. However, you can customize these values to create unique animations. Example —
// Scale down the Button when visible
ScaleAnimation(visible = buttonVisibility, startScale = 1.0f, endScale = 0.8f, animationDuration = 500) {
Button()
}
In this example, when buttonVisibility
is true
, the Button will elegantly scale down from its original size to 80% of its size within 500 milliseconds.
Fade Animation — Subtle Elegance
Fade animations gracefully blend elements in and out, providing a more seamless user experience. Let’s uncover the FadeAnimation
function.
@Composable
fun FadeAnimation(
visible: Boolean,
initialAlpha: Float = 0.0f,
targetAlpha: Float = 1.0f,
animationDuration: Int = 300,
content: @Composable () -> Unit
) {
AnimatedVisibility(
visible = visible,
enter = fadeIn(
initialAlpha = initialAlpha,
animationSpec = tween(durationMillis = animationDuration)
),
exit = fadeOut(
targetAlpha = targetAlpha
)
) {
content()
}
}
Usage and Examples
By default, FadeAnimation
will smoothly fade the content from initialAlpha
to targetAlpha
within animationDuration
milliseconds. Let's explore an example —
// Fade in an image when it becomes visible
FadeAnimation(visible = showImage, initialAlpha = 0.2f, targetAlpha = 1.0f, animationDuration = 800) {
Image(
painter = painterResource(id = R.drawable.image),
contentDescription = "My Image",
modifier = Modifier.fillMaxWidth()
)
}
Here, when showImage
becomes true
, the image will elegantly fade in from 20% opacity to 100% opacity over 800 milliseconds.
Animated Visibility?
Both ScaleAnimation
and FadeAnimation
leverage the AnimatedVisibility
composable from Jetpack Compose. This composable simplifies the process of animating UI elements based on their visibility.
To achieve this, we use the enter
and exit
parameters of AnimatedVisibility
, which take animation specifications such as scaleIn
, scaleOut
, fadeIn
, fadeOut
, etc.
Slide-In and Slide-Out Animations
To complete our Composable animation toolkit, let’s explore SlideIn
and SlideOut
animations. To achieve this, we’ll use the offset
modifier provided by Compose —
@Composable
fun SlideInOutAnimation(
visible: Boolean,
slideDirection: SlideDirection = SlideDirection.Right,
animationDuration: Int = 300,
content: @Composable () -> Unit
) {
val slideOffsetX = with(LocalDensity.current) {
when (slideDirection) {
SlideDirection.Right -> fullWidth.toPx()
SlideDirection.Left -> -fullWidth.toPx()
}
}
AnimatedVisibility(
visible = visible,
enter = slideInHorizontally(
initialOffsetX = { slideOffsetX },
animationSpec = tween(durationMillis = animationDuration)
),
exit = slideOutHorizontally(
targetOffsetX = { slideOffsetX },
animationSpec = tween(durationMillis = animationDuration)
)
) {
content()
}
}
enum class SlideDirection {
Right, Left
}
With this SlideInOutAnimation
function, we introduce a parameter slideDirection
of type SlideDirection
, which can take either SlideDirection.Right
or SlideDirection.Left
. The function calculates the appropriate offset based on the chosen direction and performs the corresponding SlideIn or SlideOut animation for the content to be rendered.
Usage and Examples
Now, let’s explore how to use the SlideInOutAnimation
function —
// SlideIn a card from the right when visible
SlideInOutAnimation(visible = showCard, slideDirection = SlideDirection.Right, animationDuration = 600) {
Card(
modifier = Modifier.fillMaxWidth().padding(16.dp),
elevation = 4.dp
) {
Text("SlideIn Card Content", modifier = Modifier.padding(16.dp))
}
}
// SlideOut a card to the left when visible
SlideInOutAnimation(visible = showCard, slideDirection = SlideDirection.Left, animationDuration = 600) {
Card(
modifier = Modifier.fillMaxWidth().padding(16.dp),
elevation = 4.dp
) {
Text("SlideOut Card Content", modifier = Modifier.padding(16.dp))
}
}
In these examples, we use the SlideInOutAnimation
function for both SlideIn and SlideOut animations. By specifying the desired SlideDirection
, the card will slide in or out accordingly.
Combining Animated Visibility and Launched Effect — A Dynamic Example
var itemVisible by remember { mutableStateOf(false) }
LaunchedEffect(
key1 = itemKey,
block = {
// Animate the item when it changes or first appears
itemVisible = false
delay(200) // Delay for visibility change to take effect
itemVisible = true
}
) {
FadeAnimation(
visible = itemVisible,
content = {
// Your item content here
},
animationDuration = 500
)
}
By leveraging AnimatedVisibility
and combining it with LaunchedEffect
, you can make your animations dynamic and interactive. For example, the item can repeatedly fade in and out if we keep toggling the itemVisible
state variable in the code.
Congrats, you can now go ahead and breathe life into your UI with engaging animations! Experiment with these animations and elevate your app’s user experience to the next level.
Remember, in the world of user experience, every pixel matters!
I am always open to collaborating with fellow developers. Feel free to connect with me / follow me on LinkedIn.
Happy Animating!