This is the scope for the content of [AnimatedVisibility].
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")
}
}
}
}