Please note that I am not talking about Nested Navigation Graphs here. I'm specifically talking about using NavHostFragments
as subsections of other NavHostFragments
and using the NavigationComponent to transition back and forth between the sub and parent fragments. That being said...
How do we properly use Navigation Component with NavHostFragments inside other NavHostFragments?
For example: Let's say I have a MainActivity
with the following layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
style="@style/Toolbar"/>
</android.support.design.widget.AppBarLayout>
<fragment
android:id="@+id/navHost"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
</LinearLayout>
Main Activity has a NavHostFragment
with the id navHost
that uses a navigation graph nav_graph
, which looks like the following:
<?xml version="1.0" encoding="utf-8"?>
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/fragmentA">
<fragment
android:id="@+id/fragmentA"
android:name="com.example.nestednavfragmentstest.FragmentA"
android:label="FragmentA"
tools:layout="@layout/fragment_a">
<action android:id="@+id/action_fragmentA_to_fragmentB" app:destination="@id/fragmentB"/>
</fragment>
<fragment
android:id="@+id/fragmentB"
android:name="com.example.nestednavfragmentstest.FragmentB"
android:label="FragmentB"
tools:layout="@layout/fragment_b"/>
</navigation>
Essentially, navHost
can navigate from FragmentA
to FragmentB
(on say, a button click). But wait, there's more...
What if I would like Fragment B
to contain it's own inner NavHostFragment
? The xml might look something like this:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fragment B"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<fragment
android:id="@+id/embeddedNavHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="50dp"
app:defaultNavHost="true"
app:layout_constraintTop_toBottomOf="@id/textView"
app:layout_constraintBottom_toBottomOf="parent"
app:navGraph="@navigation/inner_nav_graph"/>
</android.support.constraint.ConstraintLayout>
As you can see, FragmentB
has its own NavHostFragment
called embeddedNavHostFragment
, which has a navGraph that looks as follows:
<?xml version="1.0" encoding="utf-8"?>
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/inner_nav_graph"
app:startDestination="@id/innerFragmentB1">
<fragment
android:id="@+id/innerFragmentB1"
android:name="com.example.nestednavfragmentstest.InnerFragmentB1"
android:label="InnerFragmentB1"
tools:layout="@layout/inner_fragment_b1">
<action android:id="@+id/action_innerFragmentB1_to_innerFragmentB2" app:destination="@id/innerFragmentB2"/>
</fragment>
<fragment
android:id="@+id/innerFragmentB2"
android:name="com.example.nestednavfragmentstest.InnerFragmentB2"
android:label="InnerFragmentB2"
tools:layout="@layout/inner_fragment_b2"/>
</navigation>
As you can see, this inner NavHostFragment
is used to navigate between two inner fragments InnerFragmentB1
and InnerFragmentB2
. Now for the main question...
What is the proper way override onSupportNavigateUp()
in the MainActivity
?
Originally, I did something like this:
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.navHost).navigateUp()
}
But in certain cases, this would cause a crash due to back stack inconsistencies. For example:
MainActivity
starts with FragmentA
innavHost
.- Navigate via
action_fragmentA_to_fragmentB
toB
(which starts withB1
withinembeddedNavHost
) - Navigate via
action_innerFragmentB1_to_innerFragmentB2
fromB1
toB2
(occurs onembeddedNavHost
) - Press Up (
embeddedNavHost
returns fromB2
toB1
[desired outcome]) - Press Up (
navHost
returns fromB
toA
[desired outcome]) - Navigate again via
action_fragmentA_to_fragmentB
toB
IllegalArgumentException: navigation destination id/action_fragmentA_to_fragmentB
[not desired outcome]
So in the end, I ended up doing:
override fun onSupportNavigateUp(): Boolean {
val hostedFragment = navHost?.childFragmentManager?.primaryNavigationFragment
return when(hostedFragment){
is FragmentB -> {
if(hostedFragment.isInnerFragmentB2Showing()){
findNavController(R.id.embeddedNavHostFragment).navigateUp()
} else {
findNavController(R.id.navHost).navigateUp()
}
}
is FragmentA -> {
findNavController(R.id.navHost).navigateUp()
}
else -> false
}
}
This ended up working. There was no crash. But, I'm just curious as to if there's a more proper/automated way handle the up navigation of embedded NavHostFragments
. This current way feels more like a workaround than a proper solution. Are there any other alternatives to how to get this to work?
Also, I'm curious of people's opinions of NavHostFragments
being embedded within each other. Is it an appropriate use? Figuring out how to handle the back button accordingly was not an easy or straight forward solution as there was no documentation on embedding NavHostFragments
inside one another and navigating back & forth between parent and sub fragments. So, I'm wondering if the reason why there is no documentation is because maybe it's an improper (or unexpected) use of NavigationComponent and therefore not recommended.
If you'd like to take a look at my sample project, please go here