How to Use AnimatedVisibility in Jetpack Compose for Stunning UI Transitions

Table of Contents

Adding smooth motion to your Android app doesn’t just make it look “cool” — it guides the user’s eye and makes the interface feel responsive and alive. If you’ve ever felt overwhelmed by complex animation frameworks, I have great news: AnimatedVisibility in Jetpack Compose is here to do the heavy lifting for you.

In this guide, I’ll walk you through how to use AnimatedVisibility step by step. We’ll keep things simple, practical, and easy to follow. By the end, you’ll know how to create clean, engaging UI transitions without overcomplicating your code.

What is AnimatedVisibility?

AnimatedVisibility is a composable in Jetpack Compose that lets you show or hide UI elements with animation.

Instead of instantly appearing or disappearing, your UI components can:

  • Fade in or out
  • Slide in or out
  • Expand or shrink

This creates a smoother and more natural user experience.

Why Use AnimatedVisibility?

Here’s why developers love using AnimatedVisibility:

  • Makes UI feel modern and responsive
  • Improves user experience with smooth transitions
  • Easy to implement with minimal code
  • Highly customizable animations

If you’re building dropdowns, alerts, expandable cards, or onboarding flows, AnimatedVisibility is incredibly useful.

Basic Example of AnimatedVisibility

Let’s start with a simple example.

Step 1: Add a Toggle State

Kotlin
var isVisible by remember { mutableStateOf(false) }

This state controls whether the UI is visible or not.

Step 2: Use AnimatedVisibility

Kotlin
Column {
    Button(onClick = { isVisible = !isVisible }) {
        Text("Toggle Visibility")
    }

    AnimatedVisibility(visible = isVisible) {
        Text("Hello! I appear with animation.")
    }
}

Here,

  • When the button is clicked, isVisible changes
  • AnimatedVisibility reacts to that change
  • The text appears or disappears with a default animation

By default, it uses a combination of fade and expand animations.

Adding Custom Animations

The real power of AnimatedVisibility comes from customization.

You can define how elements enter and exit.

Example: Fade + Slide Animation

Kotlin
AnimatedVisibility(
    visible = isVisible,
    enter = fadeIn() + slideInVertically(),
    exit = fadeOut() + slideOutVertically()
) {
    Text("Smooth animated text!")
}
  • fadeIn() → gradually appears
  • slideInVertically() → slides from top or bottom
  • fadeOut() → fades away
  • slideOutVertically() → slides out

You can combine animations using the + operator.

Kotlin
@Composable
fun FadePlusSlideExample() {
    var isVisible by remember { mutableStateOf(false) }

    Column {
        Button(onClick = { isVisible = !isVisible }) {
            Text("Toggle Visibility")
        }

        AnimatedVisibility(
            visible = isVisible,
            enter = fadeIn() + slideInVertically(),
            exit = fadeOut() + slideOutVertically()
        ) {
            Text("Smooth animated text!")
        }
    }
}

@Preview(showBackground = true)
@Composable
fun FadePlusSlideExamplePreview() {
    CenteredPreview {
        FadePlusSlideExample()
    }
}

Controlling Animation Direction

You can customize how elements slide in.

Kotlin
slideInVertically { fullHeight -> -fullHeight }

What This Means,

  • The element enters from the top
  • -fullHeight moves it above the screen before sliding down

Similarly, you can control exit direction:

Kotlin
slideOutVertically { fullHeight -> fullHeight }

This makes it slide downward when disappearing.

Kotlin
@Composable
fun SlideDirectionExample() {
    var isVisible by remember { mutableStateOf(false) }

    Column {
        Button(onClick = { isVisible = !isVisible }) {
            Text("Toggle Visibility")
        }

        AnimatedVisibility(
            visible = isVisible,
            enter = fadeIn() + slideInVertically { fullHeight -> -fullHeight },
            exit = fadeOut() + slideOutVertically { fullHeight -> fullHeight }
        ) {
            Text("Slide Direction text!")
        }
    }
}

@Preview(showBackground = true)
@Composable
fun SlideDirectionExamplePreview() {
    CenteredPreview {
        SlideDirectionExample()
    }
}

Using AnimatedVisibility with Expand and Shrink

This is great for dropdowns or expandable content.

Kotlin
@Composable
fun ExpandShrinkExample() {
    var isExpandable by remember { mutableStateOf(false) }

    Column {
        Button(onClick = { isExpandable = !isExpandable }) {
            Text(if (isExpandable) "Shrink Content" else "Expand Content")
        }

        Spacer(modifier = Modifier.height(16.dp))

        AnimatedVisibility(
            visible = isExpandable,
            enter = expandVertically() + fadeIn(),
            exit = shrinkVertically() + fadeOut(),
        ) {
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .background(Color.LightGray)
                    .padding(16.dp)
            ) {
                Text("Expandable content goes here")
            }
        }
    }
}

@Preview(showBackground = true)
@Composable
fun ExpandShrinkExamplePreview() {
    CenteredPreview {
        ExpandShrinkExample()
    }
}

Why Use This?

  • Feels natural for lists and cards
  • Mimics real-world expansion behavior
  • Works great for FAQs or settings screens

Real-World Use Case

Let’s combine everything into a practical example.

Expandable Card

Kotlin
@Composable
fun ExpandableCard() {
    var expanded by remember { mutableStateOf(false) }

    Column(modifier = Modifier.padding(16.dp)) {
        Button(onClick = { expanded = !expanded }) {
            Text("Show Details")
        }

        AnimatedVisibility(
            visible = expanded,
            enter = fadeIn() + expandVertically(),
            exit = fadeOut() + shrinkVertically()
        ) {
            Text(
                text = "Here are more details about this item. This section expands smoothly.",
                modifier = Modifier.padding(top = 8.dp)
            )
        }
    }
}

@Preview(showBackground = true)
@Composable
fun ExpandableCardPreview() {
    CenteredPreview {
        ExpandableCard()
    }
}
  • Clean separation of state and UI
  • Smooth transition enhances usability
  • Easy to reuse in different parts of your app

AnimatedVisibility with LazyColumn

Using AnimatedVisibility inside a LazyColumn is a great way to create smooth, modern list interactions. Think expandable list items, animated insert/remove, or showing extra details per row.

You’d typically combine AnimatedVisibility with LazyColumn when:

  • Expanding/collapsing list items
  • Showing extra details on click
  • Animating conditional content inside rows

Here’s a simple example where each item expands when clicked.

Data Model

Kotlin
data class ListItem(
    val id: Int,
    val title: String,
    val description: String
)

Sample Data

Kotlin
val items = listOf(
    ListItem(1, "Item 1", "This is item 1 details"),
    ListItem(2, "Item 2", "This is item 2 details"),
    ListItem(3, "Item 3", "This is item 3 details")
)

LazyColumn with AnimatedVisibility

Kotlin
@Composable
fun ExpandableList() {
    val expandedItems = remember { mutableStateListOf<Int>() }

    LazyColumn {
        items(items) { item ->

            val isExpanded = expandedItems.contains(item.id)

            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .clickable {
                        if (isExpanded) {
                            expandedItems.remove(item.id)
                        } else {
                            expandedItems.add(item.id)
                        }
                    }
                    .padding(16.dp)
            ) {

                Text(text = item.title)

                AnimatedVisibility(
                    visible = isExpanded,
                    enter = fadeIn() + expandVertically(),
                    exit = fadeOut() + shrinkVertically()
                ) {
                    Text(
                        text = item.description,
                        modifier = Modifier.padding(top = 8.dp)
                    )
                }
            }
        }
    }
}

@Preview(showBackground = true)
@Composable
fun ExpandableListPreview() {
    CenteredPreview {
        ExpandableList()
    }
}

Best Practices for Using AnimatedVisibility

To get the most out of AnimatedVisibility, keep these tips in mind:

1. Keep Animations Subtle

Avoid overly complex animations. Simple transitions feel more professional.

2. Use Meaningful Motion

Animations should guide the user, not distract them.

3. Manage State Properly

Use remember and mutableStateOf correctly to avoid unexpected behavior.

4. Combine Animations Carefully

Too many combined effects can feel heavy. Stick to 1–2 transitions.

5. Test on Real Devices

Animations may feel different on slower devices. Always test performance.

Common Mistakes to Avoid

Here are a few pitfalls when working with AnimatedVisibility:

  • Forgetting to control state properly
  • Overusing animations in every component
  • Using heavy animations inside large lists
  • Not handling re-composition efficiently

Keep things simple and intentional.

When Should You Use AnimatedVisibility?

Use AnimatedVisibility when you need to:

  • Show/hide UI elements dynamically
  • Create expandable layouts
  • Improve onboarding screens
  • Add feedback to user actions
  • Build interactive components

If visibility changes are part of your UI, this composable is the right tool.

Conclusion

AnimatedVisibility is one of the easiest ways to bring life into your Jetpack Compose UI.

You don’t need complex animation frameworks or tons of code. With just a few lines, you can create smooth, engaging transitions that feel natural and polished.

Start small. Try a simple fade or slide. Then experiment with combinations as you get comfortable.

Skill Up: Software & AI Updates!

Receive our latest insights and updates directly to your inbox

Related Posts

error: Content is protected !!