I'm trying to make it so that when I click a button, an element disappears and then appears. I've looked for different ways to implement it, but none of them work.
I tried to implement such animation by tracking transition state, but without success. For some reason, the element appears on the same side to which it moved during the exit animation. Below is my code:
@Composable
fun Greeting() {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
val visibleState = remember { MutableTransitionState(initialState = true).apply { targetState = true } }
AnimatedVisibility(
visibleState = visibleState,
enter = slideInHorizontally(
animationSpec = tween(100, easing = LinearEasing),
initialOffsetX = { it }
),
exit = slideOutHorizontally (
animationSpec = tween(100, easing = LinearEasing),
targetOffsetX = { -it }
)
) {
RedBox()
}
Button {
visibleState.targetState = false
}
DisposableEffect(visibleState.currentState) {
onDispose {
visibleState.targetState = true
}
}
}
}
The problem with AnimatedVisibility
is that its content disappears for a moment, which can cause adjacent elements to move. As an alternative you can animate IntOffset
with a Transition
:
private enum class VisibleState { VISIBLE, EXIT, ENTER }
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
val boxSize = 50.dp
val pxToMove = with(LocalDensity.current) {
boxSize.toPx().roundToInt()
}
val visibleState = remember { MutableTransitionState(VisibleState.VISIBLE) }
val transition = rememberTransition(visibleState)
val offset by transition.animateIntOffset(
transitionSpec = {
if (VisibleState.EXIT isTransitioningTo VisibleState.ENTER) {
snap() // move from left to right immediately
} else {
tween(200, easing = LinearEasing)
}
},
label = "box animation",
) { targetState ->
when (targetState) {
VisibleState.EXIT -> IntOffset(-pxToMove, 0)
VisibleState.ENTER -> IntOffset(pxToMove, 0)
VisibleState.VISIBLE -> IntOffset.Zero
}
}
if (visibleState.isIdle) {
// If isIdle == true, it means the transition has arrived at its target state
if (visibleState.targetState == VisibleState.EXIT) {
// If target was Exit, transition to Enter (instantly move from left to right)
visibleState.targetState = VisibleState.ENTER
} else if (visibleState.targetState == VisibleState.ENTER) {
// If target was Enter, transition to Visible (animate from right to middle)
visibleState.targetState = VisibleState.VISIBLE
}
}
Box(modifier = Modifier
.clipToBounds()
.offset { offset }
.size(boxSize)
.background(Color.Red)
)
Button(onClick = { visibleState.targetState = VisibleState.EXIT }) {
Text("Toggle")
}
}