AnimatedVisibilityScope

Interface

Common
@JvmDefaultWithCompatibility
public interface AnimatedVisibilityScope

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.

Functions

public fun Modifier.animateEnterExit(
        enter: EnterTransition = fadeIn(),
        exit: ExitTransition = fadeOut(),
        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 fadeIn of the content. And the exit transition will be fading out the content using fadeOut.

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.

Code Examples

AVScopeAnimateEnterExit

@Composable
fun AVScopeAnimateEnterExit() {
    @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")
            }
        }
    }
}