17

I am using the Navigation component to show a DialogFragment (<dialog...>...</dialog> in the navigation.xml) and want to know what's the recommended way to close the Dialog. I tried myself and got the following results:

1) dismiss()in DialogFragment: seems to work fine

2) findNavController().navigateUp(): seems to work fine

3) findNavController().navigate(MyDialogFragmentDirections.actionMyDialogFragmentToMyNormalFragment()): works, but loads a fresh version of the target destination, so depending on the use case this might not be what one wants to have.

Note: My use case is that MyNormalFragmentuses MyDialogFragmentto get some input, so after MyDialogFragmentis shown, I need to get back to the already existing instance of MyNormalFragment.

So for me, only 1) or 2) is correct. Now I am wondering, is there any difference between 1) and 2) ?

stefan.at.kotlin
  • 15,347
  • 38
  • 147
  • 270

1 Answers1

36

Both 1) and 2) end up doing the same thing the end, but 2) is always a safer option.

When you call dismiss(), the DialogFragment is dismissed and the DialogFragment is stopped (it receives a callback to onStop()). This triggers the listener in DialogFragmentNavigator, which then updates the NavController's state by calling popBackStack().

dismiss() however, is an asynchronous operation (as seen in the DialogFragment source code - you'll note it does not use commitNow(), etc). Therefore if you were to check what destination you are on from the NavController.getCurrentDestination(), you'd see that you're still on the dialog destination, despite having triggered the dismissal.

navigateUp(), on the other hand, goes directly to the NavController. Since you have another destination on your back stack (the one under the DialogFragment), the NavController source code shows that navigateUp() just calls popBackStack() - the same operation that dismiss() was eventually triggering.

However, when it is the NavController that is driving the operation, NavController updates its state synchronously. This means that immediately after you call navigateUp(), it will have updated its getCurrentDestination() and internal state in addition to calling through to DialogFragmentNavigator's popBackStack(), which is what calls through to dismiss() (removing the observer mentioned above to prevent double dismissals).

Therefore calling navigateUp() is always the safer choice since it ensures that the NavController is synchronously updated to the correct state, rather than rely on FragmentManager's asynchronous timing (which may mean that additional click events are received during that time period due to multi-touch, etc.).

Calling navigate() with an action that has an app:destination on it will navigate to a new instance of the destination, which would not be appropriate for returning back to your previous instance.

ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
  • For me dismiss is not working, Current destination of navhostfragment is not updating. And I want to know if my dialog fragment is launching from navigation graph and without nav graph based on some condition then can I call findNavController().navigateUp()? – Himanshu Mar 14 '21 at 18:12
  • @Himanshu I have the same issue, being difficult to work. – Pedro Paulo Amorim Oct 21 '21 at 17:54
  • So if I have a cancel and save button, and a dismiss even when touching outside the dialog, where do I call navigateUp? My problem right now is that any of these actions remove the back button that was on my toolbar. – Chucky Jan 18 '22 at 22:50
  • @Chucky - assuming your cancel and save buttons are from an `AlertDialog`, then those buttons already internally dismiss the dialog, same as tapping outside the Dialog. Navigation still listens for those events and updates the Navigation state for you. If you `navigate()` as part of that cancel/save button though, I'd make sure that action also pops the dialog (i.e., by using `popUpTo`) - that will ensure that the dialog pop is recorded first, before you navigate to a new destination. – ianhanniballake Jan 18 '22 at 22:56
  • Thanks Hannibal. I suspected this was the case that it was already handled. But I'm still not sure why the dismiss makes my back button on the fragment underneath disappear? – Chucky Jan 18 '22 at 23:15