0

Here is a simple example to reproduce the issue I'm having:

@Composable
fun AnimatedVisibilityEx(modifier: Modifier = Modifier) {
    var visible by remember { mutableStateOf(true) }

    Box(modifier = modifier.fillMaxSize()) {
        Column(horizontalAlignment = Alignment.CenterHorizontally) {

            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .weight(1f)
                    .background(Color.LightGray)
                    .border(width = 2.dp, color = Color.Blue, shape = CircleShape)
            )

            AnimatedVisibility(
                modifier = Modifier.weight(1f),
                visible = visible
            ) {
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .background(MaterialTheme.colorScheme.primary)
                )
            }

            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .weight(1f)
                    .background(Color.LightGray)
                    .border(width = 2.dp, color = Color.Red, shape = CircleShape)
            )

            Button(
                onClick = { visible = !visible },
                modifier = Modifier.padding(top = 32.dp)
            ) {
                Text(text = "Toggle Visible")
            }
        }
    }
}

I have 3 boxes with equal weights. The middle one is going away on button click and I want a nice animation - the middle one shrinks and the other 2 expand to equally take up the space. However, there is no animation running. When I remove the weights and give them heights then everything works fine. What's the problem and how to fix it?

7
  • Did you try my answer. Does it work for you.? Commented Feb 10, 2024 at 9:56
  • 1
    It doesn't fit my needs since not all elements in column have weight Commented Feb 11, 2024 at 0:05
  • Need not have weights for all elements in column Commented Feb 11, 2024 at 1:30
  • Its only the width of the box is eithe availableWidth/3 when you toogle its availableWidth/2 as you can see in the answer Commented Feb 11, 2024 at 7:41
  • Your solution is replacing weights with calculated sizes. Imagine one of the boxes is a Text composable without a weight modifier, and its size is defined by the text itself, so it is dynamic. Then I have 2 boxes with weights. One of the boxes should be hidden. How would you calculate the width of the remaining box without knowing the width of the text? The beauty of weights is that compose will calculate the size of the Text and then give the rest of the space to the boxes according to their weights. Commented Feb 17, 2024 at 2:30

2 Answers 2

1

An ugly workaround is to remove the AnimatedVisibility and animate the weight instead:

...

    val animatedWeight: Float by animateFloatAsState(if (visible) 1f else 0.000001f)

    Box(
        modifier = Modifier
            .fillMaxWidth()
            .weight(animatedWeight)
            .background(MaterialTheme.colorScheme.primary)
    )

...

This, of course, will not allow you to use the rich set of enter/exit animations that AnimatedVisibility provides.

Sign up to request clarification or add additional context in comments.

1 Comment

Good but 0.000001f make it a little ugly and a pice of view is visible in row for example
0

How about using BoxWithConstraints?.

@Composable
fun BoxAnim() {
    var middleBoxVisible by remember { mutableStateOf(true) }

    Column {
        BoxWithConstraints {
            val middleBoxWidth = if (middleBoxVisible) maxWidth / 3 else 0.dp
            val sideBoxWidth = (maxWidth - middleBoxWidth) / 2

            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.Center
            ) {
                Box(
                    modifier = Modifier
                        .width(sideBoxWidth)
                        .height(100.dp)
                        .background(Color.Red)
                )

                AnimatedVisibility(
                    visible = middleBoxVisible,
                    enter = slideInHorizontally() + expandHorizontally(),
                    exit = shrinkHorizontally() + slideOutHorizontally()
                ) {
                    Box(
                        modifier = Modifier
                            .width(middleBoxWidth)
                            .height(100.dp)
                            .background(Color.Green)
                    )
                }

                Box(
                    modifier = Modifier
                        .width(sideBoxWidth)
                        .height(100.dp)
                        .background(Color.Blue)
                )
            }
        }

        Button(
            modifier = Modifier
                .width(200.dp)
                .height(80.dp),
            onClick = { middleBoxVisible = !middleBoxVisible }) {
            Text(text = "Toggle Middle Box")
        }
    }
    
}

The BoxWithConstraints composable is used to get the maximum width of the screen. We want to show 3 boxes equally on screen so maxWidth/3 for each of them. In case the middle is not visible we recalculate the width of the other two boxes maxWidth/2.

Update 2: Based on the comment we can measure text width and then decide the width of the middle box

@Composable
fun BoxAnim2() {
    var middleBoxVisible by remember { mutableStateOf(true) }

    var middleBoxText by remember { mutableStateOf("Hello, World! Raghunandan") } // Initial text

    val textStyle = LocalTextStyle.current

    val textMeasurer = rememberTextMeasurer()
    val textMeasured = remember(textStyle, textMeasurer) {
        textMeasurer.measure(
            text = middleBoxText,
            style = textStyle.copy(textAlign = TextAlign.Center)
        ).size.width
    }

    val newWidth = with(LocalDensity.current) { textMeasured.toDp() + 8.dp }

    Column {
        BoxWithConstraints {

            val middleBoxWidth = if (middleBoxVisible) newWidth else 0.dp
            val sideBoxWidth = ((maxWidth) - middleBoxWidth) / 2

            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.Center
            ) {
                Box(
                    modifier = Modifier
                        .width(sideBoxWidth)
                        .height(100.dp)
                        .background(Color.Red)
                )

                AnimatedVisibility(
                    visible = middleBoxVisible,
                    enter =  expandHorizontally(),
                    exit = shrinkHorizontally() 
                ) {
                    Box(
                        modifier = Modifier
                            .wrapContentWidth()
                            .height(100.dp)
                            .background(Color.Green)
                    ) {
                        Text(
                            maxLines =  1,
                            text = middleBoxText,
                            modifier = Modifier.padding(8.dp)
                        )
                    }
                }

                Box(
                    modifier = Modifier
                        .width(sideBoxWidth)
                        .height(100.dp)
                        .background(Color.Blue)
                )
            }
        }

        Button(
            modifier = Modifier
                .width(200.dp)
                .height(80.dp),
            onClick = { middleBoxVisible = !middleBoxVisible }) {
            Text(text = "Toggle Middle Box")
        }
    }

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.