Search code examples
androidandroid-fragmentsandroid-architecture-navigation

Random crashes when using androidx.navigation


while using

'androidx.navigation:navigation-ui-ktx:2.3.0' 
'androidx.navigation:navigation-fragment-ktx:2.3.0' 

the app works normally for some time until randomly i start getting

 Navigation action/destination cannot be found from the current destination NavGraph

when i click on any button that trigger a navigation, i use navigation like this

//to navigate 
var dir = MainMenuDirections.actionMainMenuToSelectPayments()
NavHostFragment.findNavController(this).navigate(dir)

//to pop current fragment
NavHostFragment.findNavController(this).popBackStack()

i tried using version 2.2.0

apparently this is a bug present since version 1.0.0 alpha 4 and it happens when you call Navigate() after calling popBackStack(), so weird how is everyone using this ?


Solution

  • Answer I get from the issue tracker if anyone makes the mistakes I made

    This is a similar case as b/128734685 where nav.popBackStack() returning false means still removes your login2 from the back stack.

    That's why the error message says:

    java.lang.IllegalArgumentException: Navigation action/destination com.dzdoes.i.payments:id/action_login2_to_registre cannot be found from the current destination NavGraph(com.dzdoes.i.payments:id/nav_graph) startDestination={Destination(com.dzdoes.i.payments:id/login2) label=Login class=com.dzdoes.i.payments.ui.Auth.Login}
    Correctly indicating that you are not on the login2 destination (you are on the root nav_graph, which correctly does not have an action named action_login2_to_registre).
    

    This is the intended behavior and, as mentioned on the other issue, the only reason you still see your fragment on the screen is to allow the activity animation to complete with the fragment still visible (rather than animating an empty activity).

    You have two options:

    Stop overriding onBackPressed(). Instead, you should follow the provide custom back navigation and register your own OnBackPressedCallback callback at the activity level with no LifecycleOwner. This will ensure that it is only triggered when there is no Navigation back stack:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
    
        onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                val builder: AlertDialog.Builder = AlertDialog.Builder(this@NavHostAuth)
                builder
                    .setMessage("are you sure you want to exit")
                    .setPositiveButton("yes") { _, _ -> finish() }
                    .setNegativeButton("no") { _, _ -> }
                    .show()
            }
        })
    }
    

    Note that you'll need to add app:defaultNavHost="true" to your XML as that is what causes NavHostFragment to correctly intercept the back button by default. This is the recommended way of achieving this functionality.

    Update your setNegativeButton to navigate to your starting destination again.

    .setNegativeButton(
        "no",
        DialogInterface.OnClickListener { dialog, which -> nav.navigate(R.id.login2) })
    

    Note that in either case, you don't ever need to call dialog.dismiss() from your setNegativeButton listener - the AlertDialog is automatically dismissed whenever the positive or negative button is pressed.