efoc
efoc

Reputation: 641

Android Navigation component: No NavHostFragments found error

I am currently implementing the Navigation Component into my app but it seems finding the NavHostFragment just doesn't work no matter what I do.

I have tried, rebuilding, invalidating, and restarting, changing the names around, and updating my Android Studio but nothing seems to work. I think this is also the reason why I get an error when trying to get the NavController in my MainActivity.kt file as it returns null.

Android Studio version 4.0.1

nav_graph.xml image:

enter image description here

nav_graph.xml

<?xml version="1.0" encoding="utf-8"?>
<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/nav_graph"
    app:startDestination="@id/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.example.movieapp.ui.main.MainFragment"
        android:label="MainFragment">
        <action
            android:id="@+id/action_mainFragment_to_searchMovieFragment"
            app:destination="@id/searchMovieFragment" />
    </fragment>
    <fragment
        android:id="@+id/searchMovieFragment"
        android:name="com.example.movieapp.ui.search.SearchMovieFragment"
        android:label="SearchMovieFragment" />
</navigation>

main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.MainActivity">

    <include
        layout="@layout/appbar" />

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

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

class MainActivity : AppCompatActivity() {

    private lateinit var toolbar: Toolbar
    private val navController by lazy {
        (supportFragmentManager.findFragmentById(R.id.main_fragment) as NavHostFragment).navController
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        toolbar = findViewById(R.id.mainToolBar)
        setSupportActionBar(toolbar)
        setupActionBarWithNavController(navController)
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        val inflater: MenuInflater = menuInflater
        inflater.inflate(R.menu.main_menu_bar, menu)
        return true
    }
}

build.gradle (:app)

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.example.app"
        minSdkVersion 23
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        debug {}

        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }

        dataBinding {
            enabled true
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    // Retrofit Libraries
    implementation 'com.squareup.retrofit2:retrofit:2.6.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.6.0'

    // Picasso Library
    implementation 'com.squareup.picasso:picasso:2.71828'
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.0'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    // Navigation libraries
    def nav_version = "2.3.0"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}

Upvotes: 16

Views: 16888

Answers (11)

Shashank Singh
Shashank Singh

Reputation: 1

You should try adding the namespace to the view

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

Example -

 <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment_container"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        app:defaultNavHost="true"
        app:navGraph="@navigation/navgraph" />

Upvotes: 0

sunflower_115
sunflower_115

Reputation: 1

Here is how you can solve that problem.

// TODO: Screen enum, enum class LunchTrayScreen(@StringRes val title: Int){,Start(title = R.string.app_name),,Entree(title = R.string.choose_entree),,SideDish(title = R.string.choose_side_dish),Accompaniment(title =R.string.choose_accompaniment), Checkout(title = R.string.order_checkout)}

// TODO: AppBar

@Composable fun LunchTrayApp() {// TODO: Create Controller and initialization val navController = rememberNavController(),val backStackEntry by navController.currentBackStackEntryAsState(),val currentScreen = LunchTrayScreen.valueOf(backStackEntry?.destination?.route ?:LunchTrayScreen.Start.name)// Create ViewModel,val viewModel: OrderViewModel = viewModel()

Scaffold(topBar = {LunchTrayAppBar(currentScreenTitle = currentScreen.title,canNavigateBack = navController.previousBackStackEntry != null,navigateUp = { navController.navigateUp() })) { innerPadding -> val uiState by viewModel.uiState.collectAsState()

// TODO: Navigation host NavHost(navController = navController,startDestination = LunchTrayScreen.Start.name,modifier = Modifier.padding(innerPadding) ){composable(route = LunchTrayScreen.Start.name){StartOrderScreen(onStartOrderButtonClicked = {navController.navigate(LunchTrayScreen.Entree.name)})} composable(route = LunchTrayScreen.Entree.name){ EntreeMenuScreen( options = com.example.lunchtray.datasource.DataSource.entreeMenuItems, onCancelButtonClicked = { viewModel.resetOrder(), navController.popBackStack(LunchTrayScreen.Start.name, inclusive = false)},onNextButtonClicked = {navController.navigate(LunchTrayScreen.SideDish.name) },onSelectionChanged = { item -> viewModel.updateEntree(item)})} composable(route = LunchTrayScreen.SideDish.name){SideDishMenuScreen( options =com.example.lunchtray.datasource.DataSource.sideDishMenuItems, onCancelButtonClicked = { viewModel.resetOrder() navController.popBackStack(LunchTrayScreen.Start.name, inclusive = false)},onNextButtonClicked = { navController.navigate(LunchTrayScreen.Accompaniment.name) },onSelectionChanged = { item -> viewModel.updateSideDish(item)} )}composable(route = LunchTrayScreen.Accompaniment.name){AccompanimentMenuScreen(options = com.example.lunchtray.datasource.DataSource.accompanimentMenuItems,onCancelButtonClicked = { viewModel.resetOrder() navController.popBackStack(LunchTrayScreen.Start.name, inclusive = false)},onNextButtonClicked = {navController.navigate(LunchTrayScreen.Checkout.name) },onSelectionChanged = { item -> viewModel.updateAccompaniment(item)} )}composable(route = LunchTrayScreen.Checkout.name){CheckoutScreen(orderUiState = uiState,onNextButtonClicked = { viewModel.resetOrder()navController.popBackStack(LunchTrayScreen.Start.name, inclusive = false)},onCancelButtonClicked = { viewModel.resetOrder()navController.popBackStack(LunchTrayScreen.Start.name, inclusive = false)}) }}}}

@Composablefun LunchTrayAppBar(@StringRes currentScreenTitle: Int, canNavigateBack : Boolean,navigateUp: () -> Unit,modifier : Modifier = Modifier){TopAppBar (title = { Text(stringResource(currentScreenTitle))},modifier = modifier,navigationIcon = {if(canNavigateBack){IconButton(onClick = navigateUp){Icon(imageVector = Icons.Filled.ArrowBack,contentDescription = stringResource(id = R.string.back_button) )}}})}

Another example would be: za currentScreen you will write CupcakeScreen//from enum class @Composablefun CupcakeApp(viewModel: OrderViewModel = viewModel(),navController: NavHostController = rememberNavController() ) {val backStackEntry by navController.currentBackStackEntryAsState() val currentScreen = CupcakeScreen.valueOf(backStackEntry?.destination?.route ?: CupcakeScreen.Start.name)Scaffold(topBar = {CupcakeAppBar(currentScreen = currentScreen,canNavigateBack = navController.previousBackStackEntry != null,navigateUp = { navController.navigateUp() } )} ) { innerPadding ->val uiState by viewModel.uiState.collectAsState()NavHost(navController = navController,startDestination = CupcakeScreen.Start.name, modifier = Modifier.padding(innerPadding)){composable(route = CupcakeScreen.Start.name){StartOrderScreen(quantityOptions = quantityOptions,onNextButtonClicked = { viewModel.setQuantity(it) navController.navigate(CupcakeScreen.Flavor.name)})}

Upvotes: -1

BlackHatSamurai
BlackHatSamurai

Reputation: 23483

I was having this issue even though I had added all of the required name and fragment suggestions from above; however, when I did an invalidate cache and restart it solved the problem.

Upvotes: 0

Yilmaz
Yilmaz

Reputation: 49182

Initally we dont have NavHostFragments. You pointed each fragment to each other, created movement actions but you need something to host this movement. That is where navigation host fragment comes into play. it is NavHostFragment that sort of swaps in our fragments as necessary inside a given layout. So what layout should hold this nav fragment. it is the layout that gets inflated during the main activity. So open activity_main.xml.

You should add Containers/NavHostFragment

enter image description here

When you choose NavHostFragment to drag, you will see the available host fragments.

Upvotes: 2

Deepak Ror
Deepak Ror

Reputation: 2234

we should make sure to write this line

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

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

Upvotes: 2

Taha Sami
Taha Sami

Reputation: 1697

For me I added android:name="androidx.navigation.fragment.NavHostFragment" inside FragmentContainerView and worked with me

Upvotes: 3

Darshn
Darshn

Reputation: 1601

As mentioned in Doc, You can also use the Layout Editor to add a NavHostFragment.

  1. In your list of project files, double-click on your activity's layout XML file to open it in the Layout Editor.
  2. Within the Palette pane, choose the Containers category, or alternatively search for "NavHostFragment".
  3. Drag the NavHostFragment view onto your activity.
  4. Next, in the Navigation Graphs dialog that appears, choose the corresponding navigation graph to associate with this NavHostFragment, and then click OK.

YOU MAY NEED TO RESTART THE ANDROID STUDIO AFTER THIS (not mentioned in the doc)

Also, you may see "fragment" instead of "FragmentContainerView" when you try to add fragment using above method.

Upvotes: 5

user3168237
user3168237

Reputation:

The way I was able to resolve the issue was by going to the Activity.xml and changing the fragment class/component tag from androidx.fragment.app.FragmentContainerView to just fragment. It then showed up in the hosts panel of the navigation graph.

Upvotes: 17

NXoc
NXoc

Reputation: 81

I had the same issue and I solved this by creating the NavHostFragment from the Design, Palette and then assign the respective navigation resource file.

Upvotes: 7

Asad Mahmood
Asad Mahmood

Reputation: 636

You can find NavController in activity as:

class MainActivity : AppCompatActivity() {

    private lateinit var toolbar: Toolbar
    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        toolbar = findViewById(R.id.mainToolBar)
        setSupportActionBar(toolbar)
        navController = findNavController(R.id.nav_host_fragment)
        setupActionBarWithNavController(navController)
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        val inflater: MenuInflater = menuInflater
        inflater.inflate(R.menu.main_menu_bar, menu)
        return true
    }
}

Upvotes: 3

Dat Pham Tat
Dat Pham Tat

Reputation: 1463

private val navController by lazy {
        (supportFragmentManager.findFragmentById(R.id.main_fragment) as NavHostFragment).navController
    }

Your navController is null because I think supportFragmentManager.findFragmentById(R.id.main_fragment) returns null. The id of your navHost is R.id.nav_host_fragment according to your activity layout.

Upvotes: 2

Related Questions