rememberTransition
Composable Function
Common
@Composable
public fun <T> rememberTransition(
transitionState: TransitionState<T>,
label: String? = null,
): Transition<T>
Creates a Transition
and puts it in the currentState
of the
provided transitionState
. If the TransitionState.targetState
changes, the Transition
will
change where it will animate to.
Remember: The provided transitionState
needs to be remember
ed.
Compared to updateTransition
that takes a targetState, this function supports a different
initial state than the first targetState. Here is an example:
In most cases, it is recommended to reuse the same transitionState
that is remember
ed, such
that Transition
preserves continuity when targetState
is
changed. However, in some rare cases it is more critical to immediately snap to a state change
(e.g. in response to a user interaction). This can be achieved by creating a new
transitionState
:
Code Examples
DoubleTapToLikeSample
@Composable
fun DoubleTapToLikeSample() {
// enum class LikedStates { Initial, Liked, Disappeared }
@Composable
fun doubleTapToLike() {
// Creates a transition state that starts in [Disappeared] State
var transitionState by remember {
mutableStateOf(MutableTransitionState(LikedStates.Disappeared))
}
Box(
Modifier.fillMaxSize().pointerInput(Unit) {
detectTapGestures(
onDoubleTap = {
// This creates a new `MutableTransitionState` object. When a new
// `MutableTransitionState` object gets passed to `updateTransition`, a
// new transition will be created. All existing values, velocities will
// be lost as a result. Hence, in most cases, this is not recommended.
// The exception is when it's more important to respond immediately to
// user interaction than preserving continuity.
transitionState = MutableTransitionState(LikedStates.Initial)
}
)
}
) {
// This ensures sequential states: Initial -> Liked -> Disappeared
if (transitionState.currentState == LikedStates.Initial) {
transitionState.targetState = LikedStates.Liked
} else if (transitionState.currentState == LikedStates.Liked) {
// currentState will be updated to targetState when the transition is finished, so
// it can be used as a signal to start the next transition.
transitionState.targetState = LikedStates.Disappeared
}
// Creates a transition using the TransitionState object that gets recreated at each
// double tap.
val transition = rememberTransition(transitionState)
// Creates an alpha animation, as a part of the transition.
val alpha by
transition.animateFloat(
transitionSpec = {
when {
// Uses different animation specs for transitioning from/to different
// states
LikedStates.Initial isTransitioningTo LikedStates.Liked ->
keyframes {
durationMillis = 500
0f at 0 // optional
0.5f at 100
1f at 225 // optional
}
LikedStates.Liked isTransitioningTo LikedStates.Disappeared ->
tween(durationMillis = 200)
else -> snap()
}
}
) {
if (it == LikedStates.Liked) 1f else 0f
}
// Creates a scale animation, as a part of the transition
val scale by
transition.animateFloat(
transitionSpec = {
when {
// Uses different animation specs for transitioning from/to different
// states
LikedStates.Initial isTransitioningTo LikedStates.Liked ->
spring(dampingRatio = Spring.DampingRatioHighBouncy)
LikedStates.Liked isTransitioningTo LikedStates.Disappeared ->
tween(200)
else -> snap()
}
}
) {
when (it) {
LikedStates.Initial -> 0f
LikedStates.Liked -> 4f
LikedStates.Disappeared -> 2f
}
}
Icon(
Icons.Filled.Favorite,
"Like",
Modifier.align(Alignment.Center)
.graphicsLayer(alpha = alpha, scaleX = scale, scaleY = scale),
tint = Color.Red,
)
}
}
}
InitialStateSample
@OptIn(ExperimentalTransitionApi::class)
fun InitialStateSample() {
// This composable enters the composition with a custom enter transition. This is achieved by
// defining a different initialState than the first target state using `MutableTransitionState`
@Composable
fun PoppingInCard() {
// Creates a transition state with an initial state where visible = false
val visibleState = remember { MutableTransitionState(false) }
// Sets the target state of the transition state to true. As it's different than the initial
// state, a transition from not visible to visible will be triggered.
visibleState.targetState = true
// Creates a transition with the transition state created above.
val transition = rememberTransition(visibleState)
// Adds a scale animation to the transition to scale the card up when transitioning in.
val scale by
transition.animateFloat(
// Uses a custom spring for the transition.
transitionSpec = { spring(dampingRatio = Spring.DampingRatioMediumBouncy) }
) { visible ->
if (visible) 1f else 0.8f
}
// Adds an elevation animation that animates the dp value of the animation.
val elevation by
transition.animateDp(
// Uses a tween animation
transitionSpec = {
// Uses different animations for when animating from visible to not visible, and
// the other way around
if (false isTransitioningTo true) {
tween(1000)
} else {
spring()
}
}
) { visible ->
if (visible) 10.dp else 0.dp
}
Card(
Modifier.graphicsLayer(scaleX = scale, scaleY = scale)
.size(200.dp, 100.dp)
.fillMaxWidth(),
elevation = elevation,
) {}
}
}