I want to create a single app activty app using Material 3 Bottom Navigation (BN) and Jetpack Navigation. Everything works fine, until one of my fragments set by the BN navigateS to another fragment. When that happens, if I change my fragment via BN again and try to go back to the other fragment, the fragment changes (which was what I expected), but the selected Icon do not (which is weird).
To make it easier to understand, think of the fragments like this:
Fragment Home
-> Fragment set by the actionHome
icon
Fragment Dashboard
-> Fragment set by the actionDashboard
icon
Fragment Notifications
-> Fragment set by the actionNotifications
icon
Fragment Home Two
-> Fragment set by a button buttonHomeTwo
on the Fragment Home
layout.
The navigation flow would be like this:
Click on the actionHome
icon
Click on the button buttonHomeTwo
Click on the actionDashboard
icon
Click on the actionHome
icon
Expected flow:
- Navigate to
Fragment Home
withactionHome
selected - Navigate to
Fragment Two
withactionHome
selected - Navigate to
Fragment Dashboard
withactionDashboard
selected - Navigate to
Fragment Two
withactionHome
selected
Actualflow (bug):
- Navigate to
Fragment Home
withactionHome
selected - Navigate to
Fragment Two
withactionHome
selected - Navigate to
Fragment Dashboard
withactionDashboard
selected - Navigate to
Fragment Two
withactionDashboard
selected <- here's the bug
To make it even easier to understand, I created a repo with this problem on Github.
But these are some sniptes from the repo:
build.gradle (app)
dependencies {
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
}
res/values/themes.xml
<resources>
<style name="Theme.BottomNavProblem" parent="Base.Theme.BottomNavProblem">
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
</style>
</resources>
res/navigation/mobile_navigation.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/mobile_navigation"
app:startDestination="@+id/navigation_home">
<fragment
android:id="@+id/navigation_home"
android:name="com.honkgoose.bottomnavproblem.ui.home.HomeFragment"
android:label="@string/title_home"
tools:layout="@layout/fragment_home" />
<fragment
android:id="@+id/navigation_dashboard"
android:name="com.honkgoose.bottomnavproblem.ui.dashboard.DashboardFragment"
android:label="@string/title_dashboard"
tools:layout="@layout/fragment_dashboard" />
<fragment
android:id="@+id/navigation_notifications"
android:name="com.honkgoose.bottomnavproblem.ui.notifications.NotificationsFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications" />
<fragment
android:id="@+id/navigation_home_two"
android:name="com.honkgoose.bottomnavproblem.ui.hometwo.HomeTwoFragment"
android:label="@string/title_home_two"
tools:layout="@layout/fragment_home_two" />
</navigation>
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navView: BottomNavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_activity_main)
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
}
res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/bottom_nav_menu" />
<fragment
android:id="@+id/nav_host_fragment_activity_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@id/nav_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
Am I doing something wrong or this is a bug in one of the libraries?
EDIT
I tried to solve the problem by using Jetpack Navigatiom Safe Args, but the same problem happened. the changes are:
build.gradle (project)
plugins {
...
id 'androidx.navigation.safeargs' version '2.5.3' apply false
}
build.gradle (app)
plugins {
...
id 'androidx.navigation.safeargs.kotlin'
}
HomeFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.homeTwoButton.setOnClickListener {
findNavController().navigate(HomeFragmentDirections.toNavigationHomeTwo())
}
}
res/navigation/mobile_navigation.xml
<fragment
android:id="@+id/navigation_home"
android:name="com.honkgoose.bottomnavproblem.ui.home.HomeFragment"
android:label="@string/title_home"
tools:layout="@layout/fragment_home" >
<action
android:id="@+id/to_navigation_home_two"
app:destination="@id/navigation_home_two" />
</fragment>
EDIT 2
I tried to solve the problem by using <FragmentContainerView>
instead of <Fragment>
, but the same problem happened. the changes are:
activity_main.xml
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container_view"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/nav_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navView: BottomNavigationView = binding.navView
val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container_view)
val navController = (fragment as NavHostFragment).navController
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}