Show confirmation on back/up in Fragment with Navigation Architecture Component

With the navigation architecture components, you can do something like this:

  1. Tell your activity to dispatch all up clicks on the home button(back arrow) to anyone listening for it. This goes in your activity.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
     if (item.itemId == android.R.id.home) {
         onBackPressedDispatcher.onBackPressed()
         return true
     }
     return super.onOptionsItemSelected(item)
}
  1. Then in your fragments, consume the events like so
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        requireActivity().onBackPressedDispatcher.addCallback(this) {
           if (*condition for showing dialog here*) {
               // Show dialog
           } else {
               // pop fragment by calling function below. Analogous to when the user presses the system UP button when the associated navigation host has focus.
               findNavController().navigateUp()
           }
        }
    }

You can used following function in onAttach in your fragment to override the onBackPressed() with help of the navigation components.

requireActivity().onBackPressedDispatcher.addCallback(
    this,
    object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            if (YOUR_CONDITION) {
                // Do something here
            } else {
                if (!findNavController().navigateUp()) {
                    if (isEnabled) {
                        isEnabled = false
                        requireActivity().onBackPressedDispatcher.onBackPressed()
                    }
                }
            }
        }
    }
)

As of androidx.appcompat:appcompat:1.1.0-beta01, in order to intercept the back button with navigation component you need to add a callback to the OnBackPressedDispatcher. This callback has to extend OnBackPressedCallback and override handleOnBackPressed. OnBackPressedDispatcher follows a chain of responsibility pattern to handle the callbacks. In other words, if you set your callback as enabled, only your callback will be executed. Otherwise, OnBackPressedDispatcher will ignore it and proceed to the next callback, and so on until it finds an enabled one (this might be useful when you have more than one callback, for instance). More info on this here.

So, in order to show your dialog, you would have to do something similar to this:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  super.onViewCreated(view, savedInstanceState)

  val callback = object : OnBackPressedCallback(true /** true means that the callback is enabled */) {
    override fun handleOnBackPressed() {
        // Show your dialog and handle navigation
    }
  }

  // note that you could enable/disable the callback here as well by setting callback.isEnabled = true/false

  requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback)
}

As for the up button, it seems like (at least for now) there aren't many possibilities. The only option I could find up until now that uses the navigation component is to add a listener for the navigation itself, which would handle both buttons at the same time:

navController.addOnDestinationChangedListener { navController, destination ->
  if (destination.id == R.id.destination) {
    // do your thing
  }
}

Regardless, this has the caveat of allowing the activity or fragment where you add the listener to know about destinations it might not be supposed to.


for up navigation simply override onOptionsItemSelected()

override fun onOptionsItemSelected(item: MenuItem): Boolean =
    when (item.itemId) {
        android.R.id.home -> {
            showDialog() // show your dialog here
            true
        }
        else -> super.onOptionsItemSelected(item)
}