12

I have a main screen that shows the general information of the app, and a bottom sheet with additional information.

The main screen content can change if the user clicks something on the navigation drawer menu, selecting a new screen.

The bottom sheet on the main screen can also change as the user clicks for more data.

So I basically have a nested Navigation in the bottom sheet, which goes inside the main navigation in the main screen.

My problem is, when using the new Android Architecture Components Navigation Controller, there's the app:defaultNavHost="true" option, which intercepts the back button.

This auto interception of the back button makes my user case pretty complicated. What I wanted is that when the user is in the main screen, the bottom sheet has control over the back button, and when the user swaps the main screen, than the main screen controls the back button.

Is it there a way to control the app:defaultNavHost programmatically, so nested Navigation Controllers can co-exist and coordinate back button ownership?

Community
  • 1
  • 1
Michel Feinstein
  • 13,416
  • 16
  • 91
  • 173
  • What version of Navigation are you using? – ianhanniballake Aug 27 '18 at 21:58
  • The most recent one. I am adapting my project to use Navigation Controllers, so I am reading about and trying to figure out the best way to accommodate it into my project – Michel Feinstein Aug 27 '18 at 22:00
  • @ianhanniballake I think I didn't make a good use of the term "nested Navigation Controllers", this might get confused to "nested graphs", maybe I should have said "embedded Navigation Controllers" to make the separation more clear...do you want me to open an issue about this case in the issue tracker of the Navigation Controller? – Michel Feinstein Aug 27 '18 at 22:56

1 Answers1

12

One of the fixes in Navigation 1.0.0-alpha04 is that Navigation automatically sets each Fragment destination as the primary navigation fragment, which ensures that any child fragment manager (such as the one used by a nested navigation graph) will automatically receive back button presses before the outer fragment manager.

This means that if you're using a NavHostFragment with app:defaultNavHost="true" within another NavHostFragment that is also using app:defaultNavHost="true", it will work out of the box. The same thing would apply if you're doing manually fragment transactions that add to the back stack using getChildFragmentManager() in any Fragment that was created by Navigation.

As explained in the original issue, app:defaultNavHost="true" is using the existing Fragment APIs and can be programmatically changed at any time by using code such as:

// This effectively removes the app:defaultNavHost flag
getSupportFragmentManager().beginTransaction()
    .setPrimaryNavigationFragment(null)
    .commit()
// Pass in your NavHostFragment to re-enable the flag
ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
  • Oh that's great to know! It would be nice if this could be added to the documentations – Michel Feinstein Aug 27 '18 at 23:32
  • What, that nested NavHostFragments just work? Or something else? – ianhanniballake Aug 27 '18 at 23:43
  • Yes, something like "Also if you want to nest NavHostFragments, they will manage the Back Stack automagically for you, as long as you set `app:defaultNavHost="true"` on all the navigation graphs you pass them. The child's NavHostFragments back stack will be cleared before returning from the parent one".....It's good to know this use case is supported, maybe some people would avoid it thinking it's complicated or bad practice. – Michel Feinstein Aug 27 '18 at 23:59
  • 8
    I have `app:defaultNavHost="true"` in my child navigation and it doesn't work. Back doesn't pop from its backstack, it's just handled by the parent. Using navigation `2.0.0`. – User May 08 '19 at 13:33
  • @Ixx - sounds like you should file a bug, including a sample project that reproduces your issue. – ianhanniballake May 09 '19 at 16:25
  • I was having the same problem as Ixx with 2.1.0. Turns out my override of `onBackPressed()` had handling for `Objects.requireNonNull(getSupportFragmentManager().findFragmentById(R.id.nav_host)).getChildFragmentManager().getBackStackEntryCount() == 0` (to confirm exit/logout) and that ignored the backstack of the child host. I found this ugly workaround (simplified): `getSupportFragmentManager().getPrimaryNavigationFragment().getChildFragmentManager().getPrimaryNavigationFragment().getChildFragmentManager().getPrimaryNavigationFragment().getChildFragmentManager().getBackStackEntryCount() == 0` – Pilot_51 Nov 21 '19 at 15:44