kotlinandroid-jetpack-composecompose-desktop

AnimatedVisibility causing layout shifts in Jetpack Compose


I'm facing an issue with AnimatedVisibility in Jetpack Compose. When an element becomes visible using AnimatedVisibility, it causes layout shifts and affects the positions of other components on the screen.

This is because AnimatedVisibility doesn't seem to reserve any space for the hidden element, leading to unexpected layout changes upon its appearance.

var isVisible by remember { mutableStateOf(false) }

Column {
    Button(onClick = { isVisible = !isVisible }) {
        Text(text = "Toggle Visibility")
    }
 
AnimatedVisibility(
    visible = isVisible,
    content = {
        Card(
             modifier = Modifier
                .fillMaxWidth()
                .height(100.dp) // Example height for the content
        ) {
            Text(text = "This content appears upon button click")
        }
    }
}

Problem: The button initially appears at the top of the screen. When clicked, the card element becomes visible using AnimatedVisibility. However, this causes the button to shift downwards, as the card doesn't seem to reserve any space while hidden. This behavior is undesirable, as it disrupts the layout flow and creates a jarring visual experience.

Questions:


Solution

  • As stated in my comment, animate visibility goes from gone to visible, if you want animation from visible to invisible, just use alpha like this:

    Column {
        var isVisible by remember { mutableStateOf(true) }
    
        Button(onClick = { isVisible = !isVisible }) {
            Text(text = "Toggle Visibility")
        }
    
        val alpha by animateFloatAsState(
            targetValue = if (isVisible) 1f else 0f,
            animationSpec = tween(durationMillis = 1000),
            label = ""
        )
    
    
        Card(
            modifier = Modifier
                .fillMaxWidth()
                .alpha(alpha)
                .height(100.dp) // Example height for the content
        ) {
            Text(text = "This content appears upon button click")
        }
    }
    

    If you want to keep Animate visibility with better animation, as you know height of card, you can set height of AnimateVisibility container:

     AnimatedVisibility(
        modifier = Modifier.height(100.dp),
        visible = isVisible,
        content = {
            Card(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(100.dp) // Example height for the content
            ) {
                Text(text = "This content appears upon button click")
            }
        }
    )