5

Using Navigation component with multiple navigation graph and BottomNavigationView similar to google navigation sample I came across such a problem.

I can't programmatically navigate from one tab in bottom navigation to another with passing argument. For example I have three nav graph and three tab in bottom navigation(one graph for each tab).

I need programmatically navigation from one tab to another so that "another" tab get argument passing from first tab.

Themelis
  • 4,048
  • 2
  • 21
  • 45
d.popovych
  • 359
  • 1
  • 3
  • 9
  • I think this question has already been answered here: https://stackoverflow.com/questions/41760893/how-to-pass-data-between-tabs – UnBanned Apr 02 '20 at 10:55

2 Answers2

0

You probably need a local Database if you have lots of data to be moved from one tab to other.as well, you can pass them with Intents. but if you don't know how to implement bottom navigation bar or tabs, I suggest you to take a look at these :

https://medium.com/@suragch/how-to-add-a-bottom-navigation-bar-in-android-958ed728ef6c

https://androidwave.com/bottom-navigation-bar-android-example/

https://medium.com/@droidbyme/android-material-design-tabs-tab-layout-with-swipe-884085ae80ff

Dharman
  • 30,962
  • 25
  • 85
  • 135
mohammad jalili
  • 187
  • 2
  • 10
0

I managed to create a workaround for it based on androidx.navigation.ui.NavigationUI internals.

Introduce following interface:

/**
 * Use this interface to navigate between bottom tabs instead of findNavController().navigate().
 *
 * Using navController's navigation can lead to incorrect app states, because each tab
 * holds its own backstack. If for example you want to open tab2 from tab1
 * by clicking button on tab1 fragment, tab2 fragment will be added to tab1 backstack.
 * In the same time tab2 will get highlighted, so clicking on tab1 to navigate back to tab1 backstack
 * will have no action, since you are already on tab1.
 *
 */
interface BottomTabNavigator {
    fun openTab(@IdRes tabId: Int, args: Bundle? = null)
}

Provide the following implementation in the place where you setup your navBar (usually MainActivity):

override fun openTab(@IdRes tabId: Int, args: Bundle?) {
    val navController = (supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment).navController // obtain nav controller. This code may differ depending on the place in which you are implementing this interface
    val menuItem = binding.bottomNav.menu.findItem(tabId) // find menu item associated with tabId, this can also slightly differ depending on how you access bottomNav
    openTab(
       menuItem = menuItem,
       navController = navController,
       args = args,
       restoreState = restoreState
    )
}

/**
* Implementation is based on [NavigationUI.onNavDestinationSelected].
* It works similarly, but additionally allows for passing args.
*/
private fun openTab(menuItem: MenuItem, navController: NavController, args: Bundle?, restoreState: Boolean): Boolean {
    val builder = NavOptions.Builder().setLaunchSingleTop(true).setRestoreState(true) // you can also add restoreState to openTab in BottomTabNavigator if you need to set it from outside

    if (menuItem.order and Menu.CATEGORY_SECONDARY == 0) {
        builder.setPopUpTo(
            destinationId = navController.graph.findStartDestination().id,
            inclusive = false,
            saveState = true
        )
    }
    return try {
       navController.navigate(
           resId = menuItem.itemId,
           args = args,
           navOptions = builder.build()
       )

       navController.currentDestination?.matchDestination(item.itemId) == true
    } catch (e: IllegalArgumentException) {
        false
    }
}

// matchDestination is internal in NavigationUI.onNavDestinationSelected, we have to port it as well.
fun NavDestination.matchDestination(@IdRes destId: Int): Boolean =
        hierarchy.any { it.id == destId }

You can later use it i.e in your Fragments like:

val bottomTabNavigator = requireActivity() as BottomTabNavigator
bottomTabNavigator.openTab(R.id.myTabId, bundleOf("myKey", "myValue")

I really wish we could just use navController.navigate() for it, but when I tried navigating to destination that is tied to my tab (as mentioned in docs). Navigation from tab1 to tab2 works, but then pressing tab1 to come back to it stays on tab2.

You can even see it in nav sample:

In Title.kt replace

view.findViewById<Button>(R.id.about_btn).setOnClickListener {
    findNavController().navigate(R.id.action_title_to_about)
}

with

view.findViewById<Button>(R.id.about_btn).setOnClickListener {
    findNavController().navigate(
        resId = R.id.list,
        args = null, // note that I don't pass args here, but it is to show case different issue
        navOptions = NavOptions.Builder().setPopUpTo(R.id.list, false).build()
    )
}

Run the app and:

  • App is opened on Home tab
  • Click about button
  • It will bring you to Leaderboard tab
  • Click Home tab
  • It will highlight Home tab, but you will stay on Leaderboard fragment

Broken bottom bar behaviour - Home is highlighted, but Leaderboard is opened

leedwon
  • 142
  • 1
  • 9