Is there a way to keep fragment alive when using BottomNavigationView with new NavController? Is there a way to keep fragment alive when using BottomNavigationView with new NavController? android android

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)    }