Home > Back-end >  Dynamically change startDestination of nested navigation graph inside of a BottomNavigationView
Dynamically change startDestination of nested navigation graph inside of a BottomNavigationView

Time:10-05

I'm trying to update my app to use BottomNavigationView. The first tab contains a HostFragment with a loading spinner that performs a network request to determine which fragment, either HomeFragment or LockedFragment, should be shown in that tab.

MainActivity handles the initial setup of the BottomNavigationView:

class MainActivity : AppCompatActivity() {

    private lateinit var navController: NavController
    private lateinit var appBarConfiguration: AppBarConfiguration

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

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

        val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_nav)
        bottomNavigationView.setupWithNavController(navController)

        appBarConfiguration = AppBarConfiguration(
            setOf(R.id.mainFragment)
        )
        setupActionBarWithNavController(navController, appBarConfiguration)
    }

My main nav graph looks like this:

<?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"
    android:id="@ id/nav_graph"
    app:startDestination="@ id/home">

    <include app:graph="@navigation/home"/>
    <include app:graph="@navigation/list"/>
    <include app:graph="@navigation/form"/>
</navigation>

with the home graph looking like:

<?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"
    android:id="@ id/home"
    app:startDestination="@ id/hostFragment">

    <fragment
        android:id="@ id/hostFragment"
        android:name="com.example.android.bottomnav.homescreen.HostFragment"
        android:label="Host">
        <action
            android:id="@ id/action_hostFragment_to_homeFragment"
            app:destination="@id/homeFragment" />
        <action
            android:id="@ id/action_hostFragment_to_lockedFragment"
            app:destination="@id/lockedFragment" />
    </fragment>

    <fragment
        android:id="@ id/homeFragment"
        android:name="com.example.android.bottomnav.homescreen.HomeFragment"
        android:label="Home" />

    <fragment
        android:id="@ id/lockedFragment"
        android:name="com.example.android.bottomnav.homescreen.LockedFragment"
        android:label="Locked"/>
</navigation>

HostFragment get's shown fine and loads it's data:

class HostFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_host, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        determineFragmentToShow()
    }

    private fun determineFragmentToShow() {
        lifecycleScope.launchWhenStarted {
            // mock network call to determine tab
            delay(1500)

            // show HomeFragment for the sake of the example, but note that
            // this would be dependent on the network call's result above
            findNavController().navigate(R.id.action_hostFragment_to_homeFragment)
        }
    }
}

which successfully navigates us to HomeFragment.

Now the problem is that whenever I press the back button from HomeFragment it goes back to HostFragment instead of closing the app.You can see the behavior in this video here.

I tried to set the popUpTo and popUpInclusive tags inside of home.xml like this:

    <fragment
        android:id="@ id/hostFragment"
        android:name="com.example.android.bottomnav.homescreen.HostFragment"
        android:label="Host">
        <action
            android:id="@ id/action_hostFragment_to_homeFragment"
            app:destination="@id/homeFragment"
            app:popUpTo="@id/home"
            app:popUpToInclusive="true"/>
        <action
            android:id="@ id/action_hostFragment_to_lockedFragment"
            app:destination="@id/lockedFragment"
            app:popUpTo="@id/home"
            app:popUpToInclusive="true"/>
    </fragment>

That got the app to close when pressing back from HomeFragment, but now each time I switch to a new tab, it creates a new instance of the fragment and adds it to the backstack. Pressing the back button then will traverse them all backwards. You can see that behavior in this video here.

So how can I update the start destination of a nested navigation graph?

I'm using the latest 2.4.0-alpha10 of navigation component so that I can get native support for multiple backstacks. Any help is greatly appreciated!

CodePudding user response:

I was able to utilize the answer here to get a solution working for me.

Inside of determineFragmentToShow() in HostFragment, I just replaced findNavController().navigate(R.id.action_hostFragment_to_homeFragment) with

val navController = findNavController()
val graph = navController.graph
val walletGraph = graph.findNode(R.id.home) as NavGraph
walletGraph.setStartDestination(R.id.homeFragment)
navController.navigate(R.id.action_hostFragment_to_homeFragment)

I still needed to include the popUpTo and popUpInclusive tags here

    <fragment
        android:id="@ id/hostFragment"
        android:name="com.example.android.bottomnav.homescreen.HostFragment"
        android:label="Host">
        <action
            android:id="@ id/action_hostFragment_to_homeFragment"
            app:destination="@id/homeFragment"
            app:popUpTo="@id/home"
            app:popUpToInclusive="true"/>
        <action
            android:id="@ id/action_hostFragment_to_lockedFragment"
            app:destination="@id/lockedFragment"
            app:popUpTo="@id/home"
            app:popUpToInclusive="true"/>
    </fragment>

but this got me the back behavior I was looking for!

  • Related