Ist es möglich, startDestination bedingt mit der Android-Navigationsarchitekturkomponente (Android Jetpack) festzulegen?
Lesezeit: 10 Minuten
Akash Patel
ich benutze Navigation aus Android-Jetpack um zwischen Bildschirmen zu navigieren. Jetzt möchte ich startDestination dynamisch setzen.
Ich habe eine Aktivität namens MainActivity und zwei Fragmente, FragmentA und FragmentB.
var isAllSetUp : Boolean = // It is dynamic and I’m getting this from Preferences.
If(isAllSetUp)
{
// show FragmentA
}
else
{
//show FragmentB
}
Ich möchte den obigen Fluss mithilfe der Navigationsarchitekturkomponente festlegen. Derzeit habe ich startDestionation wie unten verwendet, aber es erfüllt meine Anforderung nicht.
Überprüfen Sie dies unter stackoverflow.com/questions/51173002/…
– Frag Nilesh
20. August 2018 um 11:01 Uhr
Hallo @NileshRathod, es funktioniert, wenn ich zuerst FragmentA und dann FragmentB öffne. aber ich möchte StartDestination programmgesteuert festlegen. Haben Sie eine Lösung für dasselbe?
– Akash Patel
20. August 2018 um 11:16 Uhr
Versuche dies NavigationUI.onNavDestinationSelected(menuItem, navController.getNavController());
– Frag Nilesh
20. August 2018 um 11:26 Uhr
Ich möchte oben ohne Menü oder andere Navigationssteuerung auskommen. vielleicht gibt es keine Möglichkeit, startDestination bedingt zu setzen. also habe ich jetzt verwendet [stackoverflow.com/questions/51173002/… as per you suggested.
– Akash Patel
Aug 21, 2018 at 5:33
Akash Patel
Finally, I got a solution to my query…
Put below code in onCreate() method of Activity.
Kotlin code
val navHostFragment = (supportFragmentManager.findFragmentById(R.id.home_nav_fragment) as NavHostFragment)
val inflater = navHostFragment.navController.navInflater
val graph = inflater.inflate(R.navigation.nav_main)
//graph.addArgument("argument", NavArgument)
graph.setStartDestination(R.id.fragment1)
//or
//graph.setStartDestination(R.id.fragment2)
navHostFragment.navController.graph = graph
In the case of a fragment tag used instead of FragmentContainerView, the above changes remain the same
First, you need to remove app:navGraph NavHostFragment attribute from your Activity layout xml as NavHostFragment instantiates and sets the navigation graph after host Activity onCreate() call. Also if you use NavigationUI.setupWithNavController method, for example for Toolbar or BottomNavigationView, you need to put this code snippet above NavigationUI.setupWithNavController method call.
– artnest
Aug 29, 2018 at 12:12
I’d also add that to preserve back button behavior in the Kotlin code, you still need a way to set the navHostFragment as the default nav host. This can be accomplished with this transaction: supportFragmentManager.beginTransaction().setPrimaryNavigationFragment(navHostFragment).commit()
– Chris
Oct 30, 2018 at 15:47
the method graph.setDefaultArguments() doesn’t exist anymore, you also don’t need the last 2 lines (NavigationUI). See my answer for an updated version.
– Danish Khan
Jan 1, 2019 at 3:05
Is it possible to add transition animations while settings the graph?
– Maddy
Mar 25, 2020 at 7:25
Will deep linking work since it is adding startDestination when you jump to deep destination? What will it add with this setup?
– uberchilly
Aug 24, 2020 at 19:31
Some of the APIs have changed, are unavailable or are not necessary since Akash’s answer. It’s a bit simpler now.
How would I go, then, from twoFragment back to oneFragment, removing twoFragment from the back stack? I mean, the Navigation API equivalent of getSupportFragmentManager().beginTransaction().replace(R.id.nav_host_fragment, new OneFragment()).commit();? Also, could you show where to put setSupportActionBar() and NavigationUI.setupWithNavController()? Thanks 😊
– Oliver
Feb 5, 2019 at 11:15
What if the startDestination needs a bundle?
– Manikanta
Mar 25, 2019 at 10:41
@DamienLocque @Manikanta use navController.setGraph(graph, bundle)
– amitavk
Apr 12, 2019 at 11:08
use navController.setGraph(graph, intent.extras)
– Dino Sunny
Jul 17, 2020 at 5:58
LeeR
This can be done with navigation action. Because fragmentA is your start destination, so define an action in fragmentA.
Note these two fields: app:popUpToInclusive="true" app:popUpTo="@id/fragmentA"
When your MainActivity started, just do the navigation with action id, it will remove fragmentA in the stack, and jump to fragmentB. Seemingly, fragmentB is your start destination.
Thank´s, nice clean solution! You can also appearently do this in onCreate of the fragment. I´m doing it there, because I also want it to happen also when the fragment is called from the drawer menu in my case.
– Max
Jan 28, 2020 at 17:38
Though navigate() dos not kill the fragment immidiately (even if it´s removed from the backstack in the process). So, my application actually crashed in onViewCreated. I fixed it, but it seems like a bit of an ugly hack to early return out of onViewCreated, when it navigated away.
– Max
Jan 29, 2020 at 16:18
this is not an answer but Just a replication of @Akash Patel answer in more clean and clear way
// in your MainActivity
navController = findNavController(R.id.nav_host_fragment)
val graph = navController.navInflater.inflate(R.navigation.nav_graph)
if (Authentication.checkUserLoggedIn()) {
graph.startDestination = R.id.homeFragment
} else {
graph.startDestination = R.id.loginFragment
}
navController.graph = graph
Pablo Valdes
You can set your starting destination programmatically, however, most of the time your starting logic will consult some remote endpoint. If you don’t show anything on screen your UI will look bad.
What I do is always show a Splash screen. It will determine which is the next Screen to Show.
For instance, in the picture above you can ask in the Splash Screen State if there is a saved LoginToken. In case it’s empty then you navigate to the Login Screen.
Once the Login Screen is done, then you analyze the result save the Token and navigate to your Next Fragment Home Screen.
When the Back Button is Pressed in the Home Screen, you will send back a Result message to the Splash Screen that indicates it to finish the App.
To pop 1 Fragment back and navigate to another you can use the following code:
val nextDestination = if (loginSuccess) {
R.id.action_Dashboard
} else {
R.id.action_NotAuthorized
}
val options = NavOptions.Builder()
.setPopUpTo(R.id.loginParentFragment, true)
.build()
findNavController().navigate(nextDestination, null, options)
If you deep link from notification your backstack will contain splash screen as well
– uberchilly
Aug 24, 2020 at 21:44
At first I had my splash screen contain NavController instructions, but the issue comes when using asynch callbacks from reactive components…, at times, they do not reach the splash screen on time before the splash screen already took a decision. I’ve found that async direction handlers are best tied to the main activity lifeCycle since it will survive any change between splash-screen(AKA home) and any of its sub directions. There is ofc the option of clearing the cache of the publisher before reconnecting, but this is already too intrusive imo.
– Delark
Sep 24, 2021 at 17:31
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setSupportActionBar(binding.toolbar)
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
val inflater = navHostFragment.navController.navInflater
val graph = inflater.inflate(R.navigation.nav_graph)
// set initial fragment (home or login based on authorization status)
lifecycleScope.launchWhenCreated {
viewModel.isAuthorized.collect { isAuthorized ->
graph.setStartDestination(
if (isAuthorized) {
R.id.homeFragment
} else {
R.id.loginFragment
}
)
navHostFragment.navController.graph = graph
}
}
...
And it’s very important to remove app:navGraph from androidx.fragment.app.FragmentContainerView of activity layout:
and then you can remove app:startDestination="@id/homeFragment" from nav_graph.xml (cause it has no any effect anymore)
If you deep link from notification your backstack will contain splash screen as well
– uberchilly
Aug 24, 2020 at 21:44
At first I had my splash screen contain NavController instructions, but the issue comes when using asynch callbacks from reactive components…, at times, they do not reach the splash screen on time before the splash screen already took a decision. I’ve found that async direction handlers are best tied to the main activity lifeCycle since it will survive any change between splash-screen(AKA home) and any of its sub directions. There is ofc the option of clearing the cache of the publisher before reconnecting, but this is already too intrusive imo.
– Delark
Sep 24, 2021 at 17:31
12995600cookie-checkIst es möglich, startDestination bedingt mit der Android-Navigationsarchitekturkomponente (Android Jetpack) festzulegen?yes
#navigationsarchitekturkomponente #navigationsgraph
– Akash Patel
20. August 2018 um 11:00 Uhr
Überprüfen Sie dies unter stackoverflow.com/questions/51173002/…
– Frag Nilesh
20. August 2018 um 11:01 Uhr
Hallo @NileshRathod, es funktioniert, wenn ich zuerst FragmentA und dann FragmentB öffne. aber ich möchte StartDestination programmgesteuert festlegen. Haben Sie eine Lösung für dasselbe?
– Akash Patel
20. August 2018 um 11:16 Uhr
Versuche dies
NavigationUI.onNavDestinationSelected(menuItem, navController.getNavController());
– Frag Nilesh
20. August 2018 um 11:26 Uhr
Ich möchte oben ohne Menü oder andere Navigationssteuerung auskommen. vielleicht gibt es keine Möglichkeit, startDestination bedingt zu setzen. also habe ich jetzt verwendet [stackoverflow.com/questions/51173002/… as per you suggested.
Aug 21, 2018 at 5:33