65

I'm using Android Navigation Component with bottom navigation, lint gives a warning about replacing the <fragment> tag with <FragmentContainerView> but when i replaced, findNavController is not working it gives me error about it does not have a NavController set on

Fragment

<androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:navGraph="@navigation/mobile_navigation" />

Activity

val navController = findNavController(R.id.nav_host_fragment)
    
    val appBarConfiguration = AppBarConfiguration(
        setOf(
            R.id.navigation_classes, R.id.navigation_schedule, R.id.navigation_settings
        )
    )
    setupActionBarWithNavController(navController, appBarConfiguration)
    navView.setupWithNavController(navController)
}
hata
  • 11,633
  • 6
  • 46
  • 69
Jimale Abdi
  • 2,574
  • 5
  • 26
  • 33

9 Answers9

168

As per this issue, when using FragmentContainerView, you need to find the NavController using findFragmentById() rather than using findNavController() when in onCreate():

val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController

This is because findNavController(R.id.nav_host_fragment) relies on the Fragment's View to already be created which isn't the case when using FragmentContainerView (as it uses a FragmentTransaction under the hood to add the NavHostFragment).

If you are using Fragment 1.4.0 or higher and View Binding, you can simply this considerably by using the getFragment() method:

val navController = binding.container.getFragment<NavHostFragment>().navController
ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
  • Thanks, It works but Can I use `findNavController` if it's not in onCreate? – Jimale Abdi Dec 10 '19 at 21:08
  • 1
    Yes, outside of `onCreate()` the Fragment's view will be created so it'll continue to be fine to call `findNavController()` – ianhanniballake Dec 10 '19 at 21:11
  • 1
    This answer did not work inside `Fragment.onCreate()`. Says `NavController is not available before onCreate()`. – EpicPandaForce Apr 28 '20 at 19:05
  • I am seeing a similar issue @EpicPandaForce. I tried the above solution unsuccessfully. I will look for a working sample to link to here. – AdamHurwitz May 15 '20 at 17:59
  • 4
    If you're in a Fragment, you wouldn't be using anything here, you'd be using `NavHostFragment.findNavController(this)` / `findNavController()`. – ianhanniballake May 15 '20 at 18:08
  • @ianhanniballake, I'm attempting to implement within an Activity as outlined above. I will create a quick sample app to replicate the issue. – AdamHurwitz May 15 '20 at 18:26
  • @ianhanniballake, I've created a StackOverflow post and sample app outlining the issue regarding a null _NavHostFragment/NavController_, [_Null NavHostFragment/NavController with FragmentContainerView_](https://stackoverflow.com/questions/61827683/null-navhostfragment-navcontroller-with-fragmentcontainerview) – AdamHurwitz May 15 '20 at 20:23
  • Technically this is in combination with `FragmentContainerView`, and probably expected even. – EpicPandaForce May 16 '20 at 02:46
  • I figured out the issue in my [StackOverflow post](https://stackoverflow.com/a/61828944/2253682). I was not creating a parent navigation graph to define in the `FragmentContainerView`. This threw me off since the [NavigationAdvancedSample](https://github.com/android/architecture-components-samples/tree/master/NavigationAdvancedSample)'s `FragmentContainerView` does not define a parent navigation graph. – AdamHurwitz May 16 '20 at 02:49
  • 1
    @AdamHurwitz - yes, the `NavigationAdvancedSample` uses completely independent `NavHostFragment` instances to support multiple back stacks as per the [description of the sample](https://github.com/android/architecture-components-samples#samples); it uses a completely different style and functionality from any of the documentation. – ianhanniballake May 16 '20 at 05:29
  • Brilliant answer. Works for me. – Otieno Rowland Sep 10 '20 at 19:39
  • 1
    I like references to magic variables like `supportFragmentManager` – Chuck Nov 17 '20 at 19:24
  • 2
    @Chuck - that isn't a "magic variable" - that's how Kotlin code refers to the `getSupportFragmentManager()` API of `FragmentActivity`. – ianhanniballake Nov 18 '20 at 02:00
  • `navigation-fragment-ktx:2.3.2` and `FragmentContainerView` works together by defining `name` on XML: https://developer.android.com/guide/navigation/navigation-getting-started#add-navhost – Dr.jacky Dec 21 '20 at 14:37
  • implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5' implementation 'androidx.navigation:navigation-ui-ktx:2.3.5' Code you shared not working on the kotlin . On Demo example it's working fine. can you please help me. I am finding from 4 hrs. When I click on next tab. nothing happened. My parent layout is constraint layout – Dharmesh Dhameliya Aug 12 '21 at 18:20
  • only answer which worked for me. I was trying to access NavController through findNavController in onCreate() method which was causing app crash. – HemangNirmal Oct 11 '21 at 18:36
  • @ianhanniballake like you said Im using nav-frag 2.7+ and viewbinding, So i used the second method like you mentioned but it seems not working can you please tell where I gone wrong `val navController = binding.sampleHostFragment.getFragment() binding.bottomNavigation.setupWithNavController(navController)` logcat error Fatal Exception java.lang.NullPointerException: Attempt to invoke virtual method 'androidx.navigation.NavController androidx.navigation.fragment.NavHostFragment.getNavController()' on a null object reference – Nabil Nazar Aug 22 '23 at 17:10
  • @ianhanniballake as per the issue you quoted this seems working but its deprecated `Handler().post { val navController = findNavController(R.id.sampleHostFragment) binding.bottomNavigation.setupWithNavController(navController) }` handler is deprecated – Nabil Nazar Aug 22 '23 at 17:23
  • @NabilNazar - that is absolutely the wrong thing to do anyways. The code in this answer is the only thing you should be using. You'll want to create your own question with the minimal code needed to reproduce your issue if it isn't working for you. – ianhanniballake Aug 22 '23 at 20:36
10

Replace this line:

NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);

with

NavController navController = getNavController();

Where getNavController() looks like this:

    // workaround for https://issuetracker.google.com/issues/142847973
    @NonNull
    private NavController getNavController() {
        Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
        if (!(fragment instanceof NavHostFragment)) {
            throw new IllegalStateException("Activity " + this
                    + " does not have a NavHostFragment");
        }
        return ((NavHostFragment) fragment).getNavController();
    }
friederbluemle
  • 33,549
  • 14
  • 108
  • 109
4

On top of the accepted answer, a little shortcut what could be used is:

supportFragmentManager.findFragmentById(R.id.navHostMain)?.findNavController()

Regards

Joao Gavazzi
  • 1,818
  • 18
  • 8
3

Add in your build.gradle (Module: App) this line

implementation "androidx.navigation:navigation-fragment-ktx:2.3.2"

and use this in activity

val navController = supportFragmentManager.findFragmentById(R.id.your_id_nav_host_fragment)
            ?.findNavController()

and in fragment

val navController = findNavController()
Evgenii Doikov
  • 372
  • 3
  • 10
2

I solved my problen with extension:

fun FragmentActivity.findFragmentContainerNavController(@IdRes host: Int):NavController {
    try {
        val navHostFragment = supportFragmentManager.findFragmentById(host) as NavHostFragment
        return navHostFragment.findNavController()
    } catch (e: Exception) {
        throw IllegalStateException("Activity $this does not have a NavController set on $host")
    }
}
1

use simple <fragment> tag instead of <androidx.fragment.app.FragmentContainerView>

0

There's an even simpler way to @friederbluemle's answer. An assertion check should suffice.

NavHostFragment navHostFragment 
    = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
assert navHostFragment != null : "navHostFragment not found";
NavController navController = navHostFragment.getNavController();
Hank Chan
  • 1,706
  • 1
  • 16
  • 21
0

Here is the simple answer -

val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragmentContainerView)

as NavHostFragment

val navController = navHostFragment.navControllermenus.setupWithNavController(navController)

Marsad
  • 859
  • 1
  • 14
  • 35
-1

I have changed the <androidx.fragment.app.FragmentContainerView> to <fragment>, and it worked for me.