IllegalStateException: Link does not have a NavController set
AndroidAndroid Architecture-ComponentsAndroid Architecture-NavigationAndroid 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
Officially recommended solution
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
There's also an announcement from AndroidDevSummit 2019 which explains why FragmentContainerView
was introduced, and another thread on SO about the difference:
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).