How can I listen to the toolbar back press event on a multiple base node setup navigation?

18 Views Asked by At

I've seen the usual code for handling the custom back navigation for Navigation which is basically what the documentation got, but I don't think it was enough for my use case. The back button listener from the documentation does work, but the toolbar back button doesn't trigger the back button listener in my case, or won't work properly because of my use-case. My navigation is setup this way:

Login ---> MainModule1 ---> MainModule2 ---> SubModule2 to 3
  |               |                |
  |->SignUpModule |->SubModule1    |-> SubModule 4

If I go to SubModule1 and press the toolbar back button to go back to MainModule1, I noticed that the callback I passed to requireActivity().onBackPressedDispatcher.addCallback(this) isn't getting called. If I press the "physical" back button, it does get triggered.

Here's what my code looks like so far:

The code below gets called inside the MainActivity's onCreate

private fun setupNav() {
    setSupportActionBar(binding.appContentMain.toolbar)
    supportActionBar?.setDisplayHomeAsUpEnabled(true)

    appBarConfiguration = AppBarConfiguration(
        setOf(
            R.id.nav_monitoring_fragment, R.id.nav_dashboard_fragment, R.id.nav_logout, R.id.nav_login_fragment
        ), binding.drawerLayout
    )

    navController = findNavController(R.id.nav_host_fragment_content_main)
    setupActionBarWithNavController(navController, appBarConfiguration)
    binding.navView.setupWithNavController(navController)
    binding.navView.setNavigationItemSelectedListener(this)
}

Then here's the code for the Fragment that wants to listen to, hopefully, both the "physical" and toolbar back press.

override fun onAttach(context: Context) {
    super.onAttach(context)

    val onBackPressedCallback =  object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            //I want to pass back a variable from the current Fragment to MainModule1
            val action = AccountProfileFragmentDirections.goBackToDashboardFromAccountProfile(currentAccount)
            findNavController().navigate(action)
        }

    }
    requireActivity().onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
}

If I add a onOptionsItemSelected in MainActivity as well that looks like this:

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    if (item.itemId == android.R.id.home) {

        onBackPressed()
        return true
    }
    return true
}

in MainActivity, the toolbar's back button on SubModule1 does trigger the onBackPressedDispatcher callback. But if you click it again when it's on the "hamburger mode", SubModule1 gets shown again unfortunately. If I restart the app and click the hamburger menu, MainModule1 flickers which I think means it just gets reshown

1

There are 1 best solutions below

0
rminaj On

I think I already found the fix for this, and it was pretty simple.

First, we want to place the set of navigation ids we pass to AppBarConfiguration to a variable because we'll need it for something in onOptionsItemSelected. You need something like this:

private val navBaseNodeSet = setOf(
    R.id.nav_monitoring_fragment, R.id.nav_dashboard_fragment, R.id.nav_logout, R.id.nav_login_fragment
)

and of course you need to pass it to AppBarConfiguration again.

appBarConfiguration = AppBarConfiguration(navBaseNodeSet, binding.drawerLayout)

And here's the important bit, this is what onOptionsItemSelected should look like:

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    if (item.itemId == android.R.id.home) {
        return if( !navBaseNodeSet.contains(navController.currentDestination?.id) ){
            onBackPressed()
            true
        }else{
            false
        }
    }
    return false
}