App Bar Transition with Scaffold in Jetpack Compose

101 Views Asked by At

The issue I'm encountering is that when transitioning between screens, the top app bar always appears first and doesn't follow the transition of the intended screen.

How do I make the top app bar of a scaffold follow the transition of a screen as shown in the Gif?

Here's my current Application, the app bar always appears first.

Code:

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            NotesTheme {
                MainApp()
            }
        }
    }
}

@Composable
fun MainApp(
    appNavController: NavHostController = rememberNavController()
) {
    val backStackEntry by appNavController.currentBackStackEntryAsState()
    val currentScreen = backStackEntry?.destination?.route
    Scaffold(
        topBar = {
            if (currentScreen == AppScreen.Add.route) {
                TopBar(
                    navigateUp = {
                        appNavController.navigateUp()
                    },
                    isActionsActive = false,
                    isActionsClick = {}
                )
            }
        },
    ) { innerPadding ->
        AppNavHost(appNavController = appNavController, modifier = Modifier.padding(innerPadding))
    }
}

// I've provided a longer transition duration to make the difference more noticeable
fun NavGraphBuilder.addGraph(
    appNavController: NavHostController
) {
    composable(
        route = AppScreen.Add.route,
        arguments = AppScreen.Add.navArguments,
        enterTransition = {
            slideIntoContainer(
                AnimatedContentTransitionScope.SlideDirection.Start, tween(1000)
            )
        },
        exitTransition = {
            slideOutOfContainer(
                AnimatedContentTransitionScope.SlideDirection.End, tween(1000)
            )
        },
        content = {
            AddScreen(appNavController = appNavController)
        }
    )
}
1

There are 1 best solutions below

0
BenjyTec On

You apply the animations within the AppNavHost Composable. So the animations will only be applied on the Composables that you display within the NavHost, and not to the Scaffold that is on the same level as the NavHost.

To apply the animation also to the Scaffold, you have to move the topBar of the Scaffold from the MainApp Composable into a separate Scaffold in the AddScreen Composable:

@Composable
fun MainApp(
    appNavController: NavHostController = rememberNavController()
) {
    Surface(
        modifier = Modifier.fillMaxSize(),
        color = MaterialTheme.colorScheme.background
    ) { innerPadding ->
        AppNavHost(appNavController = appNavController, modifier = Modifier.padding(innerPadding))
    }
}

Then update your AddScreen Composable:

@Composable
fun AddScreen(appNavController: NavController) {
    Scaffold(
        topBar = {
            TopBar(
                navigateUp = {
                    appNavController.navigateUp()
                },
                isActionsActive = false,
                isActionsClick = {}
            )
        }
    ) { innerPadding ->
        // ...
        // your content that you previously had in here
        // don't forget to apply innerPadding to the topmost Composable in here
    }
}

As a small side note, it is a cleaner approach not to pass the NavController into a Composable. Instead, implement a callback for handling back presses in the Composable:

@Composable
fun AddScreen(onBackPressed: () -> Unit) {
    Scaffold(
        topBar = {
            TopBar(
                navigateUp = onBackPressed,
                isActionsActive = false,
                isActionsClick = {}
            )
        }
    ) { innerPadding ->
        // ...
    }
}

Then handle it in the NavHost as follows:

content = {
    AddScreen(onBackPressed = { appNavController.navigateUp() })
}