Compose: Animate 2 composables at the same time without affecting position of siblings

71 Views Asked by At

I have a row with 4 buttons, and depending on a flag isOn, a couple of buttons need to be visible/invisible with an animation. Here are the rules:

  • button 1: visible if isOn=true
  • button 2: always visible
  • button 3: visible if isOn=false
  • button 4: always visible

Here is a sample code of what I already have:

private val actionButtonSize = 67.dp

@Composable
internal fun ExampleButtons(
    modifier: Modifier = Modifier,
    isOn: Boolean,
) {
    val button3Visible by remember(isOn) {
        mutableStateOf(isOn)
    }
    val button1Visible by remember(isOn) {
        mutableStateOf(!isOn)
    }
    Box(modifier = modifier) {
        Row(
            modifier = Modifier.fillMaxSize(),
            horizontalArrangement = Arrangement.SpaceEvenly,
            verticalAlignment = Alignment.CenterVertically
        ) {
            // button 1
            AnimatedVisibility(
                visible = button1Visible,
                enter = slideInHorizontally(
                    initialOffsetX = { fullWidth -> -fullWidth }
                ) + fadeIn(),
                exit = slideOutHorizontally(
                    targetOffsetX = { fullWidth -> -fullWidth }
                ) + fadeOut()
            ) {
                IconButton(
                    modifier = Modifier
                        .size(actionButtonSize)
                        .clip(CircleShape)
                        .background(color = Color.Red),
                    onClick = { }
                ) { }
            }
            // button 2
            IconButton(
                modifier = Modifier
                    .size(actionButtonSize)
                    .clip(CircleShape)
                    .background(color = Color.Green),
                onClick = { }
            ) { }
            // button 3
            AnimatedVisibility(
                visible = button3Visible,
                enter = slideInHorizontally(
                    initialOffsetX = { fullWidth -> fullWidth }
                ) + fadeIn(),
                exit = slideOutHorizontally(
                    targetOffsetX = { fullWidth -> fullWidth }
                ) + fadeOut()
            ) {
                IconButton(
                    modifier = Modifier
                        .size(actionButtonSize)
                        .clip(CircleShape)
                        .background(color = Color.Blue),
                    onClick = { }
                ) { }
            }
            // button 4
            IconButton(
                modifier = Modifier
                    .size(actionButtonSize)
                    .clip(CircleShape)
                    .background(color = Color.Magenta),
                onClick = { }
            ) { }
        }
    }
}

@Preview(
    widthDp = 300,
)
@Composable
fun ExampleButtonsWithAnimationPreview() {
    var isOn: Boolean by remember {
        mutableStateOf(false)
    }
    Column(
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        ExampleButtons(
            modifier = Modifier
                .height(150.dp),
            isOn = isOn
        )
        Button(onClick = {
            isOn = !isOn
        }) {
            Text(
                text = if (isOn) {
                    "Off"
                } else {
                    "On"
                }
            )
        }
    }
}

The animation does what I want, but it is not smooth, and the "button 4" changes it's position during the animation, while I would like it to be at the same position everytime.

Here is how it looks like now:

enter image description here

What am I missing here?

0

There are 0 best solutions below