Search code examples
androidandroid-jetpackandroid-jetpack-navigation

Multiple Host Fragments - Navigation Android Jetpack


I want to have a multiple host_fragment to have many bottomNavigationView. One of the tabs (Nº2 "Myzone") go to another fragment which is my second host_fragment ("profile_host_fragment") it has another bottomNavigationView at the top. What I was trying to do is this: enter image description here main_nav_graph

<navigation
    xmlns:app="http://schemas.android.com/apk/res-auto"
    app:startDestination="@id/feature_home_nav_graph">
    <include app:graph="@navigation/feature_home_nav_graph" />
    <include app:graph="@navigation/feature_my_zone_nav_graph" />
    <include app:graph="@navigation/feature_catalogue_nav_graph" />
    <include app:graph="@navigation/feature_cart_nav_graph" />
    <include app:graph="@navigation/feature_support_nav_graph" />
</navigation>

my_zone_nav_graph (where profile tab is)

<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"
    app:startDestination="@id/myZoneFragment"
    android:id="@+id/feature_my_zone_nav_graph">

    <fragment
        android:id="@+id/myZoneFragment"
        android:name="feature_my_zone.presentation.myzone.MyZoneFragment"
        android:label="fragment_my_zone"
        tools:layout="@layout/fragment_my_zone">
   
        <action
            android:id="@+id/action_myZoneFragment_to_profileHostFragment"
            app:destination="@id/profileHostFragment" />
    </fragment>

    <fragment
        android:id="@+id/profileHostFragment"
        android:name="feature_my_zone.presentation.profile.ProfileHostFragment"
        android:label="fragment_profile_host"
        tools:layout="@layout/fragment_profile_host" />

</navigation>

profile_nav_graph

<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/profile_nav_graph.xml"
    app:startDestination="@id/addressFragment">

    <fragment
        android:id="@+id/addressFragment"
        android:name="feature_my_zone.presentation.profile.address.AddressFragment"
        android:label="fragment_address"
        tools:layout="@layout/fragment_address" />
    <fragment
        android:id="@+id/profileFragment"
        android:name="feature_my_zone.presentation.profile.profile.ProfileFragment"
        android:label="fragment_profile"
        tools:layout="@layout/fragment_profile" />
    <fragment
        android:id="@+id/paymentsFragment"
        android:name="feature_my_zone.presentation.profile.payments.PaymentsFragment"
        android:label="fragment_payments"
        tools:layout="@layout/fragment_payments" />

    <fragment
        android:id="@+id/shippingFragment"
        android:name="feature_my_zone.presentation.profile.shipping.ShippingFragment"
        android:label="fragment_shipping"
        tools:layout="@layout/fragment_shipping" />
</navigation>

this is my fragment_profile_host

<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context="feature_my_zone.presentation.profile.ProfileHostFragment">

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/topNavigationView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:labelVisibilityMode="labeled"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:menu="@menu/profile_nav_menu" />

    <fragment
        android:id="@+id/profile_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/topNavigationView"
        app:navGraph="@navigation/profile_nav_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>

and I am trying to set up nav controller on ProfileHostFragment this way like in the same that in NavHostActivity

  val navController = profile_nav_host_fragment.findNavController()
        topNavigationView.setupWithNavController(navController)

But I am getting the following error:

Process: game.spa.android.app.v3, PID: 22367
java.lang.IllegalStateException: profile_nav_host_fragment must not be null
    at feature_my_zone.presentation.profile.ProfileHostFragment.setupTopNavigation(ProfileHostFragment.kt:22)
    at feature_my_zone.presentation.profile.ProfileHostFragment.onViewCreated(ProfileHostFragment.kt:17)

I Don't know what is the propper way to achieve this. Thanks for any help.


Solution

  • Why IllegalStateException happens?

    Basically, thing is that when we have nested navigation host fragments then retrieving child NavHost fragment from Fragment class throws this exception. refer here

    Official doc states that:

    fun Fragment.findNavController(): NavController

    Calling this on a Fragment that is not a NavHostFragment or within a NavHostFragment will result in an IllegalStateException


    Solution is to retrieve it from activity context and navigation library provides method for that (refer here):

    fun Activity.findNavController(@IdRes viewId: Int): NavController

    Find a NavController given the id of a View and its containing Activity.

    Calling this on a View that is not a NavHost or within a NavHost will result in an IllegalStateException.

    So, changing

    val navController = profile_nav_host_fragment.findNavController()
    

    to

    val navController = activity.findNavController(R.id.profile_nav_host_fragment)
    

    will help solving the exception.