15

I am using Navigation Component for navigating in my app. It works fine inside fragments but it fails to find the nav host in the activity that holds the actual navigation host.

I am trying to open a new fragment when the user clicks on FAB, which I included in Main activity's XML. When I call findNavController() it fails to find the controller. The nav host controller is in the XML layout. I can't understand why it fails to find it.

MainActivity

class MainActivity : AppCompatActivity(), OnActivityComponentRequest {
    override fun getTabLayout(): TabLayout {
        return this.tabLayout
    }

    override fun getFap(): FloatingActionButton {
        return this.floatingActionButton
    }

    private lateinit var tabLayout: TabLayout
    private lateinit var floatingActionButton: FloatingActionButton

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)
        this.tabLayout = tabs
        this.floatingActionButton = fab

        fab.setOnClickListener {

         it.findNavController().navigate(R.id.addNewWorkoutFragment)

        }
    }
}

Activity main XML

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
        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:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".domain.MainActivity"
        android:animateLayoutChanges="true">

    <com.google.android.material.appbar.AppBarLayout
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:layout_scrollFlags="scroll|enterAlways"
                app:popupTheme="@style/AppTheme.PopupOverlay"/>

        <com.google.android.material.tabs.TabLayout
                android:id="@+id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

            <com.google.android.material.tabs.TabItem
                    android:text="Test 1"
                    android:layout_height="match_parent"
                    android:layout_width="match_parent"/>

            <com.google.android.material.tabs.TabItem
                    android:text="Test 2"
                    android:layout_height="match_parent"
                    android:layout_width="match_parent"/>
        </com.google.android.material.tabs.TabLayout>

    </com.google.android.material.appbar.AppBarLayout>

      <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:navGraph="@navigation/main_navigation" />


    <com.google.android.material.bottomappbar.BottomAppBar
            android:id="@+id/bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"/>


    <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_anchorGravity="right|top"
            app:layout_anchor="@+id/bar"
            android:src="@drawable/ic_add_black_24dp"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>
Vadim Kotov
  • 7,103
  • 8
  • 44
  • 57
Jeremi
  • 962
  • 1
  • 11
  • 29

7 Answers7

13

In Java you can find NavController inside activity this way:

Navigation.findNavController(this,R.id.nav_host).navigate(R.id.YourFragment);
zmag
  • 6,257
  • 12
  • 26
  • 33
JunaidKhan
  • 525
  • 5
  • 11
9

Try setting up onClickListener of Fab button in onStart of the Activity as in onCreate Activity is just inflating the View and haven't set the NavHostController. So if you setup onClickListener in onStart of activity is will work as expected.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setSupportActionBar(toolbar)
    this.tabLayout = tabs
    this.floatingActionButton = fab
  }

override fun onStart() {
    super.onStart()
    floatingActionButton.setOnClickListener {
      it.findNavController().navigate(R.id.addNewWorkoutFragment)
    }
  }
Anmol
  • 5,619
  • 3
  • 28
  • 54
  • Now SOF has thank's feature next to answer u can use that. :P anyway's ur WC @KhamidjonKhamidov – Anmol Jul 13 '20 at 15:47
9

I was getting the same exeception until I managed to get the navController this way:

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

Obtained from this answer:

Before finding the nav controller, you need to set the view, if you are using binding:

binding = BaseLayoutBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)

If not:

setContentView(R.layout.activity_main)

Use the corresponding binding for fragments.

LightMan
  • 3,068
  • 26
  • 26
  • 2
    This is actually what is stated in navigation component documentation: https://developer.android.com/guide/navigation/navigation-getting-started#navigate "When creating the NavHostFragment using FragmentContainerView or if manually adding the NavHostFragment to your activity via a FragmentTransaction, attempting to retrieve the NavController in onCreate() of an Activity via Navigation.findNavController(Activity, @IdRes int) will fail. You should retrieve the NavController directly from the NavHostFragment instead." – Socram Mar 23 '21 at 10:47
2

Turns out that activity that holds navigation controller... doesn't have navigation component.

The solution is to manually set the NavController to each view contained in the activity.

val navController = findNavController(R.id.nav_host_fragment)
Navigation.setViewNavController(fab, navController)

Now this would work:

fab.setOnClickListener {

    it.findNavController().navigate(R.id.addNewWorkoutFragment)

}

I still don't understand why this works the way it works so any explanation would be more than welcome :)

As of now, Android API simply doesn't make much sense.

Source: Navigate to fragment on FAB click (Navigation Architecture Components)

Jeremi
  • 962
  • 1
  • 11
  • 29
1

The problem might be that FAB was added to the activity or a fragment different from the one used by NavHost fragment. In this case, when you call it.findNavController() it cannot find the navigation controller.

You can either check that your FAB belongs to the fragment pulled by NavHost or call an Activity's findNavController(<id>) and pass it id of the fragment you're looking up

override fun onCreate(savedInstanceState: Bundle?) {
        ...
        fab.setOnClickListener {
            findNavController(R.id.nav_host_fragment)
                .navigate(R.id.addNewWorkoutFragment)

        }
    }
straya
  • 4,814
  • 1
  • 25
  • 33
1

you can still get access in your oncreate by doing

    //the id would be the id of the Fragment Element In Your Activity XML File
    val navGraph = Navigation.findNavController(this, R.id.activity_main)
    binding.mainFab.setOnClickListener {
        navGraph.navigate(R.id.destinationFragment)
    }
1

I found this answer helpful. In your activity paste this code

Navigation.findNavController(this, R.id.nav_host_fragment).navigate(R.id.yourDestination)
lvl3ha
  • 19
  • 1