IllegalStateException: Link does not have a NavController set

AndroidAndroid Architecture-ComponentsAndroid Architecture-Navigation

Android Problem Overview


I'm using Android Navigation Component for Navigation. I have a LoginFragment which has a button to transition to SignUpFragment. On clicking the button I'm getting this error.

java.lang.IllegalStateException: View android.support.v7.widget.AppCompatButton{49d9bd1 VFED..C.. ...P.... 201,917-782,1061 #7f090172 app:id/signUpLink} does not have a NavController set

Here is my nav_graph.xml

<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        app:startDestination="@id/loginFragment">
        <fragment
            android:id="@+id/loginFragment"
            android:name="org.fossasia.openevent.app.core.auth.login.LoginFragment"
            android:label="login_fragment"
            tools:layout="@layout/login_fragment">
            <action
                android:id="@+id/action_loginFragment_to_signUpFragment"
                app:destination="@id/signUpFragment" />
          
        </fragment>
    </navigation>

Here is the code in LoginFragment for Navigation -

binding.signUpLink.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_loginFragment_to_signUpFragment, null));

Here is extract from activity layout file for NavHostFragment -

<FrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    android:name="android.navigation.fragment.NavHostFragment"
    app:navGraph="@navigation/main_navigation"
    app:defaultNavHost="true"/>

Android Solutions


Solution 1 - Android

Currently using the FragmentContainerView is not very friendly, you have to access it from the supportFragmentManager:

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

In my xml my FragmentContainerView looks like this:

<androidx.fragment.app.FragmentContainerView
    android:id="@+id/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_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toBottomOf="parent"
    app:navGraph="@navigation/nav_graph"
    />

This has been tested with androidx navigation version 2.3.0

I've removed the old answer because it has been clarified by Google devs that it is not the recommended solution anymore. Thanks @Justlearnedit, for posting a comment allowing me to update this issue.

Solution 2 - Android

UPDATED SOLUTION

Actually, Navigation can't find NavController in FrameLayout. So replacing <FrameLayout> with <fragment> will make it work.

Add the following inside the <fragment> tag -

android:name="androidx.navigation.fragment.NavHostFragment"

After doing the changes, the code will look similar to this -

 <fragment
       android:id="@+id/fragment_container"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       app:layout_behavior="@string/appbar_scrolling_view_behavior"
       android:name="androidx.navigation.fragment.NavHostFragment"
       app:navGraph="@navigation/main_navigation"
       app:defaultNavHost="true"/>

Solution 3 - Android

After doing some research I found this Issue also on Google's bugtracker. So here's the official solution in Java:

NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager()
                .findFragmentById(R.id.nav_host_fragment);
NavController navCo = navHostFragment.getNavController();

and here in Kotlin:

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

Solution 4 - Android

The reason is that the fragment view isn't available inside the Activity.onCreate() method if you're adding it using FragmentContainerView (or just a FrameLayout). The proper way to get the NavController in this case is to find the NavHostFragment and get the controller from it. See the issue and the explanation.

override fun onCreate(savedInstanceState: Bundle?) {
   ...

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

Don't use <fragment> instead of <androidx.fragment.app.FragmentContainerView> as some other answers suggest. See the comment from the Google team in the issue I mentioned above.

> You should always use FragmentContainerView. There are absolutely other fixes around window insets and layout issues that occur when a fragment's root layout is directly within other layouts such as ConstraintLayout, besides the underlying issues with the tag where fragments added via that tag go through lifecycle states entirely differently from the other Fragments added to the FragmentManager. The Lint check is there exactly because you absolutely should switch over to FragmentContainerView in all cases.

There's also an announcement from AndroidDevSummit 2019 which explains why FragmentContainerView was introduced, and another thread on SO about the difference: vs as a view for a NavHost

Solution 5 - Android

Just replace <FrameLayout> With <fragment> and replace android:name="org.fossasia.openevent.app.core.auth.login.LoginFragment" with android:name="androidx.navigation.fragment.NavHostFragment"

Solution 6 - Android

Use the view of fragment such as onViewCreated

 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
  
    val navController = Navigation.findNavController(view)

    binding.signUpLink.setOnClickListener {
            navController.navigate(R.id.action_loginFragment_to_signUpFragment)
    }
   

Solution 7 - Android

I faced the same problem. So,instead of this,

binding.signUpLink.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_loginFragment_to_signUpFragment, null));

I used my NavHostFragment to find the NavHostFragment:

Button button = (Button)findViewById(R.id.button);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                
                Fragment navhost = getSupportFragmentManager().findFragmentById(R.id.fragment2);
                NavController c = NavHostFragment.findNavController(navhost);
                c.navigate(R.id.firstFragment);


            }
        });

fragment2 is navhostfragmentID.

Solution 8 - Android

In my case it was done by android studio...

<fragment
        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:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />

The above code is the working code that was replaced as shown below by a warning!

<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:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />

Solution 9 - Android

In Java try this below line:

Navigation.findNavController(findViewById(R.id.nav_host_fragment)).navigate(R.id.first_fragment);

Solution 10 - Android

I had the following error:

> MainActivity@9ff856 does not have a NavController set on 2131230894

I was using Bottom Navigation View:

The following worked for me:

val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavigationView)

val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment)
val navController = navHostFragment?.findNavController()
if (navController != null) {
    bottomNavigationView.setupWithNavController(navController)
}

Solution 11 - Android

A weird thing happens to me, below code snippet was working on normal flow from Fragment1 to fragment2, but after coming to fragment1 and on again navigate fragment2, this was throwing the "Navigation controller not set for the view" error.

    binding.ivIcon.setOnClickListener(v -> {
            Openfragment2(v);});

private void Openfragment2(View view) {
    Navigation.findNavController(binding.ivIcon).navigate(R.id.fragment2);

}

Here problem was in view, in findNavController need to pass the onclicked view.

private void Openfragment2(View view) {
    Navigation.findNavController(view).navigate(R.id.fragment2);

}

Solution 12 - Android

i faced this issue just now, but i was sure about my code and then realized that i have changed the location of the fragment from under the main package to another folder

so i solved the issue with Build-> clean then Build ->make project to let the IDE to change its Directions class

Solution 13 - Android

In my case I change my code

val action =
        StartFragmentDirections.actionStartFragmentToLoginFragment()
    Navigation.findNavController(view).navigate(action);

In onViewCreated()

Solution 14 - Android

this code should work properly for kotlin

val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_loginFlow_fragment) as NavHostFragment
val navController = navHostFragment.navController
navController.navigate(R.id.to_dictionaryDownload1)

Solution 15 - Android

in my case (I was using BottomNavigation), in findNavController I was adding Framlayout Id! you should add the fragment Id like this:

in my xml:

<FrameLayout
            android:id="@+id/container_orders"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@+id/bnv_main"
            android:visibility="gone">

            <fragment
                android:id="@+id/nav_orders"
                android:name="androidx.navigation.fragment.NavHostFragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:defaultNavHost="false" />

        </FrameLayout>

in Kotlin class:

private val navOrdersController by lazy {
        mainActivity.findNavController(R.id.nav_orders).apply {
            graph = navInflater.inflate(R.navigation.main_navigation_graph).apply {
                startDestination = startDestinations.getValue(R.id.action_history)
            }
        }
    }

Solution 16 - Android

private lateinit var binding : ActivityNewsBinding

lateinit var viewModel: NewsViewModel

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    //setContentView(R.layout.activity_news)
    binding = ActivityNewsBinding.inflate(layoutInflater)
    setContentView(binding.root)

    val newsrepository = NewsRepository(ArticleDatabase(this))
    val viewModelProviderFactory = NewsViewModelProviderFactory(newsrepository)
    viewModel = ViewModelProvider(this,viewModelProviderFactory).get(NewsViewModel::class.java)

    
    val navHostFragment = supportFragmentManager.findFragmentById(R.id.newsNavHostFragment) as NavHostFragment
    val navController = navHostFragment.navController
    binding.bottomNavigationView.setupWithNavController(navController)


}

Solution 17 - Android

Updated answer for FragmentViewContainer

Make sure you have

android:name="androidx.navigation.fragment.NavHostFragment"

instead of the fragment you want as start fragment. (Start destination is defined in the Nav Graph).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMasqueradeView Question on Stackoverflow
Solution 1 - AndroidRawaView Answer on Stackoverflow
Solution 2 - AndroidMasqueradeView Answer on Stackoverflow
Solution 3 - AndroidBananaView Answer on Stackoverflow
Solution 4 - AndroidValeriy KatkovView Answer on Stackoverflow
Solution 5 - AndroidShivam GoelView Answer on Stackoverflow
Solution 6 - AndroidAli HasanView Answer on Stackoverflow
Solution 7 - Androidbutchy butchyView Answer on Stackoverflow
Solution 8 - AndroidRamhawkz47View Answer on Stackoverflow
Solution 9 - AndroidAndresElCaraDeMemelaView Answer on Stackoverflow
Solution 10 - AndroidJason BraithwaiteView Answer on Stackoverflow
Solution 11 - Androidu_pendraView Answer on Stackoverflow
Solution 12 - AndroidAmr263View Answer on Stackoverflow
Solution 13 - AndroidDave RinconView Answer on Stackoverflow
Solution 14 - AndroidVishal MishraView Answer on Stackoverflow
Solution 15 - AndroidSana EbadiView Answer on Stackoverflow
Solution 16 - AndroidMuhammad WaqasView Answer on Stackoverflow
Solution 17 - AndroidOtziiiView Answer on Stackoverflow