Search code examples
androidbottomnavigationviewandroid-architecture-navigationandroid-navigationandroid-bottomnav

Android clear backstack after reselecting Bottom Navigation tab


Using the newest Navigation Component with the BottomNavigationView, the NavController now saves and restores the states of tabs by default:

As part of this change, the NavigationUI methods of onNavDestinationSelected(), BottomNavigationView.setupWithNavController() and NavigationView.setupWithNavController() now automatically save and restore the state of popped destinations, enabling support for multiple back stacks without any code changes. When using Navigation with Fragments, this is the recommended way to integrate with multiple back stacks.

This is great! Now switching tabs gives you the last viewed stack.

But, if the user reselects a tab, say they've gone Home -> Detail Page A -> Detail Page B, then they select the Home tab expecting to go back to the default view, they still see Detail Page B.

It seems like part of the discussion was to handle the "reselecting a tab" behavior as mentioned in the issue tracker, but I can't figure out the recommended way for implementing this.

All that's included in the NavigationAdvancedSample is:

val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_nav)
bottomNavigationView.setupWithNavController(navController)

// Setup the ActionBar with navController and 3 top level destinations
appBarConfiguration = AppBarConfiguration(
        setOf(R.id.titleScreen, R.id.leaderboard,  R.id.register)
    )
setupActionBarWithNavController(navController, appBarConfiguration)

And this just restores the previous state, as noted in the release notes.

How can we check for tapping a nav bar item a second time and clear the back stack?


Solution

  • BottomNavigationView has its own method for handling reselection via setOnItemReselectedListener() (or, when using an earlier version of the Material Design Library, the now deprecated setOnNavigationItemReselectedListener()).

    bottomNavigationView.setupWithNavController does not set this listener (as there is no Material specification for exactly what reselecting a tab should do), so you need to set it yourself:

    val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_nav)
    bottomNavigationView.setupWithNavController(navController)
    
    // Add your own reselected listener
    bottomNavigationView.setOnItemReselectedListener { item ->
        // Pop everything up to the reselected item
        val reselectedDestinationId = item.itemId
        navController.popBackStack(reselectedDestinationId, inclusive = false)
    }