81

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"/>
Priyanshu Khandelwal
  • 2,018
  • 2
  • 16
  • 33
  • 6
    I also had got this issue. But in my case, the error is caused because I placed the Button wrongly in _activity.xml file at the same level with "androidx.navigation.fragment.NavHostFragment" fragment instead in _fragment.xml file. Now I moved the "Button" to _fragment.xml file and it's working. I'm not sure this can help you or not. Just sharing about the issue that I have been faced :-) – BomberBus May 25 '18 at 17:02
  • 3
    Make sure you use `fragment` element any not other Layout element – onmyway133 Aug 06 '19 at 20:58

13 Answers13

183

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.

Rawa
  • 10,673
  • 6
  • 33
  • 52
  • 10
    see here for reason why this is probably good advice: https://stackoverflow.com/questions/58703451/fragmentcontainerview-as-navhostfragment – hmac Mar 19 '20 at 09:28
  • 2
    In the issue linked they discuss why you shouldn't use fragment: https://issuetracker.google.com/issues/142847973#comment15: *The Lint check is there exactly because you absolutely should switch over to FragmentContainerView in all cases.* – Friendly Banana Aug 10 '20 at 14:17
  • 2
    @Justlearnedit Thanks, this was not clear at the point when I posted this answer. Thanks for updating me, I've tested the recommended solution and made sure to update the post to reflect it. – Rawa Aug 12 '20 at 06:10
  • "Method invocation 'getNavController' may produce 'NullPointerException'" so we always have to check for null before as well? – Big_Chair Oct 16 '20 at 17:23
  • @Big_Chair Not sure what method you are referring to. NavHostFragment has a getNavController() function which is annotated with NonNull. Maybe you are using an older version of Navigation? – Rawa Nov 01 '20 at 19:16
  • 1
    The recommended solution worked for me on Nav 2.3.3, thank you for pointing that out! – JCrooks Feb 08 '21 at 17:56
  • @Rawa It is not working for me. I am testing with navigation `2.3.5`. I have seen many posts with this solution, Indeed Android documentation states like you had posted. – Israel May 09 '21 at 12:33
  • This also works when you receive null after minified or ofuscation. Try this. – jan4co May 13 '21 at 05:40
65

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"/>
Priyanshu Khandelwal
  • 2,018
  • 2
  • 16
  • 33
  • 48
    This works fine, but will give a lint warning on the use of the tag, stating that it should be replaced with a FragmentContainerView (although doing so will make your app crash). – Peter Nov 13 '19 at 10:00
  • 1
    android:name="androidx.navigation.fragment.NavHostFragment" – heLL0 Dec 17 '19 at 15:52
  • 2
    @Peter I was able to solve this by upgrading gradle depedencies of material to `implementation 'com.google.android.material:material:1.2.0-alpha04'`. – Johannes Büttner Jan 30 '20 at 23:11
  • 3
    had an exception with FragmentContainerView, but works fine with fragment. Also when used FragmentContainerView it give lint warning that app:navGraph not set, but no such warning with fragment – Siarhei Apr 17 '20 at 11:17
  • 3
    Check this answer and its comments: https://stackoverflow.com/a/59275182/421467 – Dr.jacky Dec 21 '20 at 14:09
28

I've done some research and found this Issue also on Google. 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
Friendly Banana
  • 1,424
  • 6
  • 17
19

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

Valeriy Katkov
  • 11,518
  • 2
  • 37
  • 69
5

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)
    }
Ali hasan
  • 543
  • 8
  • 8
4

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.

ॐ Rakesh Kumar
  • 1,188
  • 1
  • 11
  • 21
4

In Java try this below line:

Navigation.findNavController(findViewById(R.id.nav_host_fragment)).navigate(R.id.first_fragment);
Jaimil Patel
  • 1,058
  • 4
  • 11
2

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

Shivam Goel
  • 107
  • 1
  • 5
1

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" />
Ramhawkz47
  • 11
  • 1
1

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(R.id.bottomNavigationView)

val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment)
val navController = navHostFragment?.findNavController()
if (navController != null) {
    bottomNavigationView.setupWithNavController(navController)
}
Amir Dora.
  • 2,358
  • 4
  • 28
  • 47
0

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);

}
u_pendra
  • 708
  • 7
  • 22
0

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

Amr263
  • 71
  • 1
  • 6
0

In my case i change my code

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

in onViewCreated

Dave Rincon
  • 230
  • 3
  • 8