androidanimationandroid-jetpack-compose

BottomNavigation animation for some reason adds white background before animation starts


I try to add animation to botton navigation appearence slide from the bottom

There is code:

@Composable
private fun BottomNavImpl() {
    Scaffold(
        bottomBar = {
            var isBottomBarVisible: Boolean by remember { mutableStateOf(false) }

            LaunchedEffect(Unit) {
                delay(2000)
                isBottomBarVisible = true
            }

            AnimatedVisibility(
                visible = isBottomBarVisible,
                enter = slideInVertically(
                    initialOffsetY = { it },
                    animationSpec = tween(
                        durationMillis = 500,
                        easing = FastOutSlowInEasing
                    )
                ),
                exit = slideOutVertically(
                    targetOffsetY = { it },
                    animationSpec = tween(
                        durationMillis = 300,
                        easing = FastOutSlowInEasing
                    )
                )
            ) {
                NavigationBar(
                    containerColor = Color.Red,
                ) {
                    repeat(3) {
                        NavigationBarItem(
                            icon = {
                                Icon(
                                    modifier = Modifier,
                                    painter = painterResource(android.R.drawable.ic_menu_add),
                                    contentDescription = null,
                                )
                            },
                            selected = false,
                            onClick = { }
                        )
                    }
                }
            }
        },
        content = { paddingValues ->
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(paddingValues)
                    .background(Color.Green)
            ) {

            }
        }
    )
}

It provides such a result:

enter image description here

So, the problem is that the white background appears first for the full height and then animation.

What am I missing?


Solution

  • Found out that the issue in the "paddingValues" once "bottomBar" visibility changes, "Scaffold" updates "paddingValues" that cause that "background" appears before animation starts.

    Fix is animate "paddingValues" as well:

    class MainActivity : ComponentActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            enableEdgeToEdge()
    
    
            setContent {
                Test_delete_itTheme {
                    var isBottomBarVisible: Boolean by remember { mutableStateOf(false) }
    
                    LaunchedEffect(Unit) {
                        while (true) {
                            delay(2000)
                            isBottomBarVisible = !isBottomBarVisible
                        }
                    }
    
                    Scaffold(
                        modifier = Modifier.fillMaxSize(),
                        bottomBar = {
                            AnimatedVisibility(
                                modifier = Modifier,
                                visible = isBottomBarVisible,
                                enter = slideInVertically(
                                    initialOffsetY = { it },
                                    animationSpec = tween(
                                        durationMillis = 500,
                                        easing = FastOutSlowInEasing
                                    )
                                ),
                                exit = slideOutVertically(
                                    targetOffsetY = { it },
                                    animationSpec = tween(
                                        durationMillis = 300,
                                        easing = FastOutSlowInEasing
                                    )
                                )
                            ) {
                                NavigationBar(
                                    containerColor = Color.Red,
                                ) {
                                    repeat(3) {
                                        NavigationBarItem(
                                            modifier = Modifier,
                                            icon = {
                                                Icon(
                                                    modifier = Modifier,
                                                    painter = painterResource(android.R.drawable.ic_menu_add),
                                                    contentDescription = null,
                                                )
                                            },
                                            selected = false,
                                            onClick = { }
                                        )
                                    }
                                }
                            }
                        }
                    ) { innerPadding ->
                        val targetPadding: PaddingValues = if (isBottomBarVisible) {
                            innerPadding
                        } else {
                            PaddingValues(
                                top = innerPadding.calculateTopPadding(),
                                bottom = 0.dp,
                                start = innerPadding.calculateStartPadding(LayoutDirection.Ltr),
                                end = innerPadding.calculateEndPadding(LayoutDirection.Ltr)
                            )
                        }
    
                        val animatedStartPadding by animatePaddingValue(targetPadding.calculateStartPadding(LayoutDirection.Ltr), isBottomBarVisible)
                        val animatedTopPadding by animatePaddingValue(targetPadding.calculateTopPadding(), isBottomBarVisible)
                        val animatedEndPadding by animatePaddingValue(targetPadding.calculateEndPadding(LayoutDirection.Ltr), isBottomBarVisible)
                        val animatedBottomPadding by animatePaddingValue(targetPadding.calculateBottomPadding(), isBottomBarVisible)
    
                        Box(
                            modifier = Modifier
                                .fillMaxSize()
                                .padding(
                                    start = animatedStartPadding,
                                    top = animatedTopPadding,
                                    end = animatedEndPadding,
                                    bottom = animatedBottomPadding
                                )
                                .background(Color.Green)
                        ) {
    
                        }
                    }
                }
            }
        }
    }
    
    @Composable
    private fun animatePaddingValue(targetValue: Dp, isBottomBarVisible: Boolean): androidx.compose.runtime.State<Dp> {
        val animationSpec = tween<Dp>(
            durationMillis = if (isBottomBarVisible) 500 else 300,
            easing = FastOutSlowInEasing
        )
        return animateDpAsState(targetValue = targetValue, animationSpec = animationSpec, label = "")
    }