Reputation: 3890
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.test.oe.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"/>
Upvotes: 185
Views: 161236
Reputation: 173
Make sure your not using
<androidx.fragment.app.FragmentContainerView
android:id="@+id/frgContainerHome"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"/>
Use :
<fragment
android:id="@+id/frgContainerHome"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"/
Upvotes: 2
Reputation: 3890
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"/>
Upvotes: 135
Reputation: 111
Instantiate your lateinit var navController
in onPostCreate()
instead of onCreate()
like so:
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
navController = findNavController(this, R.id.nav_fragment_container)
}
make sure your in your xml you are using:
<androidx.fragment.app.FragmentContainerView />
Upvotes: 2
Reputation: 51
I have also faced this problem. My scenario was when i click on image in recycler view go to another fragment. below code was giving error
When i change this code to below code it works well. NO ERROR
Upvotes: 0
Reputation: 302
in my case, I forget to add the start destination
app:startDestination="@+id/navigation_home"
res ==> navigation ==> mobile_navigation.xml
<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/mobile_navigation.xml"
app:startDestination="@+id/navigation_home">
Upvotes: 0
Reputation: 1
For this to work you have to override both onCreatedView and onViewCreated in all the fragments my code: `
<FrameLayout
android:id="@+id/fl"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/main_nav_graph"
app:defaultNavHost="true"/>
</FrameLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="@menu/bottom_nav_menu" />
Upvotes: 0
Reputation: 2496
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).
Upvotes: 0
Reputation: 239
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)
}
Upvotes: 0
Reputation: 2533
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
Upvotes: 74
Reputation: 7220
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)
}
}
}
Upvotes: 0
Reputation: 81
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" />
Upvotes: 8
Reputation: 368
In my case I change my code
val action =
StartFragmentDirections.actionStartFragmentToLoginFragment()
Navigation.findNavController(view).navigate(action);
In onViewCreated()
Upvotes: 0
Reputation: 119
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)
}
Upvotes: 5
Reputation: 1
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)
Upvotes: 0
Reputation: 375
Just replace <FrameLayout>
With <fragment>
and replace android:name="org.fossasia.openevent.app.core.auth.login.LoginFragment"
with android:name="androidx.navigation.fragment.NavHostFragment"
Upvotes: 7
Reputation: 40702
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: <androidx.fragment.app.FragmentContainerView> vs as a view for a NavHost
Upvotes: 49
Reputation: 13936
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.
Upvotes: 355
Reputation: 61
In Java try this below line:
Navigation.findNavController(findViewById(R.id.nav_host_fragment)).navigate(R.id.first_fragment);
Upvotes: 6
Reputation: 151
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
Upvotes: 0
Reputation: 61
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
.
Upvotes: 6
Reputation: 948
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);
}
Upvotes: 0
Reputation: 673
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)
}
Upvotes: 5