Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BottomNavigationView.setupWithNavController not working

Currently I'm developing an Android app, in which I would like to utilize both Android Navigation Component and BottomNavigationView.

While working on said app I checked out the official codelab and several questions here, but they didn't prove to be of any help.

The problem is that assigning navController to my BottomNavigationView does not seem to have any effect - clicking on menu items does not affect the navigation host.

My code:

main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayoutxmlns: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=".MainActivity">

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

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/main_nav_bnv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:background="@android:color/transparent"
        android:elevation="0dp"
        app:elevation="0dp"
        app:menu="@menu/menu_main" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

MainActivity.kt

import android.os.Bundle
import android.os.PersistableBundle
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.NavigationUI
import androidx.navigation.ui.setupWithNavController
import com.aredruss.qurio.databinding.ActivityMainBinding
import com.aredruss.qurio.helpers.viewBinding

class MainActivity : AppCompatActivity() {

    // ViewBindingDelegate
    private val binding: ActivityMainBinding by viewBinding(ActivityMainBinding::inflate)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    
    }

    // Using onPostCreate because accessing NavHost in OnCreate causes crashes
    override fun onPostCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
        super.onPostCreate(savedInstanceState, persistentState)
        binding.navigationBb.setupWithNavController(findNavController(R.id.main_host_fr))
        NavigationUI.setupWithNavController(binding.mainNavBnv, findNavController(R.id.main_host_fr))
    }
}

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/action_history">

    <fragment
        android:id="@+id/action_history"
        android:name="com.aredruss.qurio.ui.HistoryFragment"
        android:label="HistoryFragment"
        tools:layout="@layout/fragment_history" >
    </fragment>
  
    <fragment
        android:id="@+id/action_settings"
        android:name="com.aredruss.qurio.ui.SettingsFragment"
        android:label="SettingsFragment"
        tools:layout="@layout/fragment_settings" >
    </fragment>
</navigation>

menu_main.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_history"
        android:icon="@drawable/ic_history"
        android:title="History"
        app:showAsAction="withText" />

    <item
        android:id="@+id/action_settings"
        android:title="Settings"
        android:icon="@drawable/ic_settings"
        app:showAsAction="withText"
        />
</menu>

I've checked out this solution and it wasn't of any help.

like image 857
Alexander Medyanik Avatar asked Jul 26 '26 05:07

Alexander Medyanik


2 Answers

You've overridden the wrong onPostCreate():

override fun onPostCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {

You don't want the one that takes a PersistableBundle - you want the regular one:

override fun onPostCreate(savedInstanceState: Bundle?) {

Now your onPostCreate() will actually be called.

Of course, you shouldn't be using onPostCreate() at all. Instead, you should be following the documentation, which specifically state that you should be using the following code to get your NavController in onCreate():

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

which lets you write your entire code as:

// ViewBindingDelegate
private val binding: ActivityMainBinding by viewBinding(ActivityMainBinding::inflate)

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Note how we actually set the content view from the binding
    setContentView(binding.root)

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

    binding.navigationBb.setupWithNavController(navController)
    binding.mainNavBnv.setupWithNavController(navController)
}
like image 159
ianhanniballake Avatar answered Jul 28 '26 22:07

ianhanniballake


Here are the concerns that need to be fixed:

  • You are using ViewBindingDelegate which automatically handles the setContentView, so you need to remove this, as you won't be able to use binding.xx in this case:
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // setContentView(R.layout.activity_main) //<<<<< remove this
    
}
  • You use onPostCreate while it should not be implemented by developers, and only need to be touched by the system:

Called when activity start-up is complete (after onStart() and onRestoreInstanceState(Bundle) have been called). Applications will generally not implement this method; it is intended for system classes to do final initialization after application code has run.

As per my test, onPostCreate() didn't get called. So, to fix this, move this part of code to a lifecycle method that can be implemented by developers like onStart() or onResume():

override fun onStart() {
    super.onStart()
    NavigationUI.setupWithNavController(
        binding.mainNavBnv,
        findNavController(R.id.main_host_fr)
    )
}

This way the NavController can auto handle the BottomNavigationView:

UPDATE: As per comments, you should get the NavController using the NavHostFragment as follows, and therefore you can transfer the code to the onCreate() method:

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

like image 40
Zain Avatar answered Jul 28 '26 22:07

Zain



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!