Is there a way to keep fragment alive when using BottomNavigationView with new NavController?
Try this.
Navigator
Create custom navigator.
@Navigator.Name("custom_fragment") // Use as custom tag at navigation.xmlclass CustomNavigator( private val context: Context, private val manager: FragmentManager, private val containerId: Int) : FragmentNavigator(context, manager, containerId) { override fun navigate(destination: Destination, args: Bundle?, navOptions: NavOptions?) { val tag = destination.id.toString() val transaction = manager.beginTransaction() val currentFragment = manager.primaryNavigationFragment if (currentFragment != null) { transaction.detach(currentFragment) } var fragment = manager.findFragmentByTag(tag) if (fragment == null) { fragment = destination.createFragment(args) transaction.add(containerId, fragment, tag) } else { transaction.attach(fragment) } transaction.setPrimaryNavigationFragment(fragment) transaction.setReorderingAllowed(true) transaction.commit() dispatchOnNavigatorNavigated(destination.id, BACK_STACK_DESTINATION_ADDED) }}
NavHostFragment
Create custom NavHostFragment.
class CustomNavHostFragment: NavHostFragment() { override fun onCreateNavController(navController: NavController) { super.onCreateNavController(navController) navController.navigatorProvider += PersistentNavigator(context!!, childFragmentManager, id) }}
navigation.xml
Use custom tag instead of fragment tag.
<navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/navigation" app:startDestination="@id/navigation_first"> <custom_fragment android:id="@+id/navigation_first" android:name="com.example.sample.FirstFragment" android:label="FirstFragment" /> <custom_fragment android:id="@+id/navigation_second" android:name="com.example.sample.SecondFragment" android:label="SecondFragment" /></navigation>
activity layout
Use CustomNavHostFragment instead of NavHostFragment.
<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"> <fragment android:id="@+id/nav_host_fragment" android:name="com.example.sample.CustomNavHostFragment" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toTopOf="@+id/bottom_navigation" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/navigation" /> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_navigation" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:menu="@menu/navigation" /></androidx.constraintlayout.widget.ConstraintLayout>
Update
I created sample project. link
I don't create custom NavHostFragment. I use navController.navigatorProvider += navigator
.
Update 19.05.2021 Multiple backstack
Since Jetpack Navigation 2.4.0-alpha01 we have it out of the box.Check Google Navigation Adavanced Sample
Old answer:
Google samples linkJust copy NavigationExtensions to your application and configure by example. Works great.
After many hours of research I found solution. It was all the time right in front of us :) There is a function: popBackStack(destination, inclusive)
which navigate to given destination if found in backStack. It returns Boolean, so we can navigate there manually if the controller won't find the fragment.
if(findNavController().popBackStack(R.id.settingsFragment, false)) { Log.d(TAG, "SettingsFragment found in backStack") } else { Log.d(TAG, "SettingsFragment not found in backStack, navigate manually") findNavController().navigate(R.id.settingsFragment) }