AnimatedVisibilityScope

Known direct subclasses
AnimatedContentScope

Receiver scope for content lambda for AnimatedContent.


This is the scope for the content of AnimatedVisibility. In this scope, direct and indirect children of AnimatedVisibility will be able to define their own enter/exit transitions using the built-in options via Modifier.animateEnterExit. They will also be able define custom enter/exit animations using the transition object. AnimatedVisibility will ensure both custom and built-in enter/exit animations finish before it considers itself idle, and subsequently removes its content in the case of exit.

Note: Custom enter/exit animations that are created independent of the AnimatedVisibilityScope.transition will have no guarantee to finish when exiting, as AnimatedVisibility would have no visibility of such animations.

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.FloatingActionButton
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AnimatedVisibilityScope.Item(
    modifier: Modifier,
    backgroundColor: Color
) {
    // Creates a custom enter/exit animation for scale property.
    val scale by transition.animateFloat { enterExitState ->
        // Enter transition will be animating the scale from 0.9f to 1.0f
        // (i.e. PreEnter -> Visible). Exit transition will be from 1.0f to
        // 0.5f (i.e. Visible -> PostExit)
        when (enterExitState) {
            EnterExitState.PreEnter -> 0.9f
            EnterExitState.Visible -> 1.0f
            EnterExitState.PostExit -> 0.5f
        }
    }

    // Since we defined `Item` as an extension function on AnimatedVisibilityScope, we can use
    // the `animateEnterExit` modifier to produce an enter/exit animation for it. This will
    // run simultaneously with the `AnimatedVisibility`'s enter/exit.
    Box(
        modifier.fillMaxWidth().padding(5.dp).animateEnterExit(
            // Slide in from below,
            enter = slideInVertically(initialOffsetY = { it }),
            // No slide on the way out. So the exit animation will be scale (from the custom
            // scale animation defined above) and fade (from AnimatedVisibility)
            exit = ExitTransition.None
        ).graphicsLayer {
            scaleX = scale
            scaleY = scale
        }.clip(RoundedCornerShape(20.dp)).background(backgroundColor).fillMaxSize()
    ) {
        // Content of the item goes here...
    }
}

@Composable
fun AnimateMainContent(mainContentVisible: MutableTransitionState<Boolean>) {
    Box {
        // Use the `MutableTransitionState<Boolean>` to specify whether AnimatedVisibility
        // should be visible. This will also allow AnimatedVisibility animation states to be
        // observed externally.
        AnimatedVisibility(
            visibleState = mainContentVisible,
            modifier = Modifier.fillMaxSize(),
            enter = fadeIn(),
            exit = fadeOut()
        ) {
            Box {
                Column(Modifier.fillMaxSize()) {
                    // We have created `Item`s below as extension functions on
                    // AnimatedVisibilityScope in this example. So they can define their own
                    // enter/exit to run alongside the enter/exit defined in AnimatedVisibility.
                    Item(Modifier.weight(1f), backgroundColor = Color(0xffff6f69))
                    Item(Modifier.weight(1f), backgroundColor = Color(0xffffcc5c))
                }
                // This FAB will be simply fading in/out as specified by the AnimatedVisibility
                FloatingActionButton(
                    onClick = {},
                    modifier = Modifier.align(Alignment.BottomEnd).padding(20.dp),
                    backgroundColor = MaterialTheme.colors.primary
                ) { Icon(Icons.Default.Favorite, contentDescription = null) }
            }
        }

        // Here we can get a signal for when the Enter/Exit animation of the content above
        // has finished by inspecting the MutableTransitionState passed to the
        // AnimatedVisibility. This allows sequential animation after the enter/exit.
        AnimatedVisibility(
            // Once the main content is visible (i.e. targetState == true), and no pending
            // animations. We will start another enter animation sequentially.
            visible = mainContentVisible.targetState && mainContentVisible.isIdle,
            modifier = Modifier.align(Alignment.Center),
            enter = expandVertically(),
            exit = fadeOut(animationSpec = tween(50))
        ) {
            Text("Transition Finished")
        }
    }
}

Summary

Public functions

open Modifier

animateEnterExit modifier can be used for any direct or indirect children of AnimatedVisibility to create a different enter/exit animation than what's specified in AnimatedVisibility.

Cmn

Public properties

Transition<EnterExitState>

transition allows custom enter/exit animations to be specified.

Cmn

Public functions

animateEnterExit

@ExperimentalAnimationApi
open fun Modifier.animateEnterExit(
    enter: EnterTransition = fadeIn() + expandIn(),
    exit: ExitTransition = fadeOut() + shrinkOut(),
    label: String = "animateEnterExit"
): Modifier

animateEnterExit modifier can be used for any direct or indirect children of AnimatedVisibility to create a different enter/exit animation than what's specified in AnimatedVisibility. The visual effect of these children will be a combination of the AnimatedVisibility's animation and their own enter/exit animations.

enter and exit defines different EnterTransitions and ExitTransitions that will be used for the appearance and disappearance animation. There are 4 types of EnterTransition and ExitTransition: Fade, Expand/Shrink, Scale and Slide. The enter transitions can be combined using +. Same for exit transitions. The order of the combination does not matter, as the transition animations will start simultaneously. See EnterTransition and ExitTransition for details on the three types of transition.

By default, the enter transition will be a combination of fadeIn and expandIn of the content from the bottom end. And the exit transition will be shrinking the content towards the bottom end while fading out (i.e. fadeOut + shrinkOut). The expanding and shrinking will likely also animate the parent and siblings if they rely on the size of appearing/disappearing content.

In some cases it may be desirable to have AnimatedVisibility apply no animation at all for enter and/or exit, such that children of AnimatedVisibility can each have their distinct animations. To achieve this, EnterTransition.None and/or ExitTransition.None can be used for AnimatedVisibility.

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun FullScreenNotification(visible: Boolean) {
    AnimatedVisibility(
        visible = visible,
        enter = fadeIn(), exit = fadeOut()
    ) {
        // Fade in/out the background and foreground
        Box(Modifier.fillMaxSize().background(Color(0x88000000))) {
            Box(
                Modifier.align(Alignment.TopStart).animateEnterExit(
                    // Slide in/out the rounded rect
                    enter = slideInVertically(),
                    exit = slideOutVertically()
                ).clip(RoundedCornerShape(10.dp)).requiredHeight(100.dp)
                    .fillMaxWidth().background(Color.White)
            ) {
                // Content of the notification goes here
            }
        }
    }
}

Public properties

transition

@ExperimentalAnimationApi
val transitionTransition<EnterExitState>

transition allows custom enter/exit animations to be specified. It will run simultaneously with the built-in enter/exit transitions specified in AnimatedVisibility.