18

I need to display custom AlertDialog, but only when there are no more fragments after calling NavController.navigateUp(). My current code does something similar, but there is a bug to it:

override fun onBackPressed() {

    if (navController.navigateUp()) {
        return
    }

    showQuitDialog()
}

This somewhat works, but if I cancel the AlertDialog and don't quit the app, navController already navigated up to NavGraph root, which is not the fragment in which I was when AlertDialog appeared. So, if I try to use any navigation action from that fragment, I get error:

java.lang.IllegalArgumentException: navigation destination com.example.test/actionNavigateToNextFragment is unknown to this NavController

This could be resolved, if I had access to mBackStack field in NavController, but it's package private, and I can't use reflection in my current project. But if it was public, I would use it the following way:

override fun onBackPressed() {

    // 2 because first one is NavGraph, second one is last fragment on the stack
    if(navController.mBackStack.size > 2) { 
        navController.navigateUp()
        return
    }

    showQuitDialog()
}

Is there a way to do this without reflection?

xinaiz
  • 7,744
  • 6
  • 34
  • 78

4 Answers4

48

You can compare the ID of the start destination with the ID of the current destination. Something like:

override fun onBackPressed() = when {
    navController.graph.startDestination == navController.currentDestination?.id -> showQuitDialog()
    else -> super.onBackPressed()
}

Hope it helps.

3

Try this for any destination (you can find your destination id in the navigation graph):

private fun isDesiredDestination(): Boolean {
    return with(navController) {
        currentDestination == graph[R.id.yourDestinationId]
    }
}
Boken
  • 4,825
  • 10
  • 32
  • 42
Manzur Alahi
  • 1,870
  • 23
  • 19
1

Comparing IDs of destination may not be the best solution because you may have multiple screens with the same ID but different arguments on the backstack.

Here's an alternative:

val isRootScreen = navController.previousBackStackEntry == null
Saket
  • 2,945
  • 1
  • 29
  • 31
0

if you want this is a button so to speak you can have this

yourbutton.setOnClickListener {
            val currentFragment = navController.currentDestination?.id
           when (navController.graph.startDestination == currentFragment) {
                true ->  { //Go here}
                 // Go to the app home
                else -> onBackPressed()
            }
        }
FEELIX
  • 407
  • 4
  • 8