How to Keep Fragment Alive When Using Bottomnavigationview with New Navcontroller

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.xml
class 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.

Save state of fragment A when navigating from bottomNavigationView to fragment B

Few facts around Android Navigation Component with Bottom Navigation Bar.

- > Fragments are always recreated (onCreate, onViewCreated, onViewDestroyed are called as soon as the user navigates to another fragment)

- > Fragment will save its state only when activity is recreated (e.g. screen rotation) , navigating between fragments doesn't save fragment's state.

I want to save the state of this fragment (with the text there) when
navigating to another fragment and the coming back to the fragment A
(and have the text still there from the previous button click).

This wont be possible as a new instance of FragmentA is created when
you navigate back from fragmentB to Fragment A.

you can not achieve this with navigation controller as of now.

Since you are using navigation component, You should switch to using Viewmodel to solve retain fragment state issue.

Consult the following link to see how to communicate and save data between fragments using viewmodel.

https://androidwave.com/fragment-communication-using-viewmodel/

Android navigation component: how save fragment state

As per the open issue, Navigation does not directly support multiple back stacks - i.e., saving the state of stack B when you go back to B from A or C since Fragments do not support multiple back stacks.

As per this comment:

The NavigationAdvancedSample is now available at https://github.com/googlesamples/android-architecture-components/tree/master/NavigationAdvancedSample

This sample uses multiple NavHostFragments, one for each bottom navigation tab, to work around the current limitations of the Fragment API in supporting multiple back stacks.

We'll be proceeding with the Fragment API to support multiple back stacks and the Navigation API to plug into it once created, which will remove the need for anything like the NavigationExtensions.kt file. We'll continue to use this issue to track that work.

Therefore you can use the NavigationAdvancedSample approach in your app right now and star the issue so that you get updates for when the underlying issue is resolved and direct support is added to Navigation.

BottomNavigationView with navhostfragment not showing fragment, android?

I am not sure what is the issue with the bottom navigation view with this use case. After going through multiple links and posts I was able to solve

You need an extension for bottom navigation which takes up list of nav graphs.
I found that piece in one of the links

Extension
BottomNavigationExt.kt

Your XML will look like

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="home.HomeFragment">

<androidx.fragment.app.FragmentContainerView
android:id="@+id/homeNavHost"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottomMenu"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/bottom_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>

Finally setup with

val navGraphIds = listOf(
R.navigation.dashboard_nav_graph,
R.navigation.offer_nav_graph,
R.navigation.profile_nav_graph,
R.navigation.document_nav_graph,
R.navigation.more_nav_graph
)

// Setup the bottom navigation view with a list of navigation graphs
val controller = binding.bottomMenu.setupWithNavController(
navGraphIds = navGraphIds,
fragmentManager = childFragmentManager,
containerId = R.id.homeNavHost,
intent = requireActivity().intent
)

The most important your menu item id and graph id should be same



Related Topics



Leave a reply



Submit