I have problem with navigation component.
I have 2 nav graph: nav1 and nav2
In nav2 start destination by default is FragmentA, but sometimes I need to start from FragmentB. I use this code (this code in MainActivity):
if(isTest) {
val navController = findNavController(navigationFragment())
val navGraph = navController.navInflater.inflate(R.navigation.nav)
navGraph.startDestination = R.id.fragmentB
findNavController(navigationFragment()).graph = navGraph
}
But there is error like this -> java.lang.IllegalArgumentException: No destination with ID 2131362873 is on the NavController's back stack
nav2 code here:
android:id="#+id/nav2"
app:startDestination="#id/FragmentA">
<fragment
android:id="#+id/FragmentA"
android:name="......./FragmentA"
android:label="FragmentA" />
<fragment
android:id="#+id/FragmentB"
android:name="......./FragmentB"
android:label="FragmentB" />
From nav1 action:
<action
android:id="#+id/action_global_link_test"
app:destination="#id/nav2"
app:enterAnim="#anim/enter_from_right"
app:exitAnim="#anim/exit_from_left"
app:popEnterAnim="#anim/enter_from_left"
app:popExitAnim="#anim/exit_from_right" />
Related
I have a navigation xml for a feature as below -
<?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/navigation_featureB.xml"
app:startDestination="#id/FragmentB">
<fragment
android:id="#+id/FragmentB"
android:name="FragmentB"
android:label="FragmentB">
<deepLink app:uri="App://FragmentB" />
</fragment>
</navigation>
In my FeatureA fragment, I do the following -
val uri = Uri.parse("App://FragmentB")
findNavController().navigate(uri)
I know how to use safeArgs without deep link.
How do I pass data to other feature with deep link?
You can do the following
First add argument in navigation.xml
<?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/navigation_featureB.xml"
app:startDestination="#id/FragmentB">
<fragment
android:id="#+id/FragmentB"
android:name="FragmentB"
android:label="FragmentB">
<argument
android:name="yourarg"
android:defaultValue="Argument Default Value"/>
<deepLink app:uri="App://FragmentB/{yourarg}" />
</fragment>
</navigation>
Then you can call it like this
val args = Bundle()
args.putString("yourarg", "Argument Value")
val deeplink = findNavController().createDeepLink()
.setDestination(R.id.FragmentB)
.setArguments(args)
.createPendingIntent()
val builder = NotificationCompat.Builder(
context, "deeplink")
.setContentTitle("Deep link with data")
.setContentText("Deep link with data to Android")
.setSmallIcon(R.drawable.ic_android)
.setContentIntent(deeplink)
.setAutoCancel(true)
val notificationManager =
context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(0, builder.build())
EDITED
Without Notification
First, add argument in navigation.xml
<?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/navigation_featureB.xml"
app:startDestination="#id/FragmentB">
<fragment
android:id="#+id/FragmentB"
android:name="FragmentB"
android:label="FragmentB">
<argument
android:name="yourarg"
android:defaultValue="Argument Default Value"/>
<deepLink app:uri="App://FragmentB/{yourarg}" />
</fragment>
</navigation>
Then you can call it like this
val uri = Uri.parse("App://FragmentB/yourarg")
findNavController().navigate(uri)
Then you can get the argument with this code
val argument = arguments?.getString("yourarg")
// Or with Safe Args
val safeArgs: FragmentBArgs by navArgs()
val yourarg = safeArgs.yourarg
References :
https://codelabs.developers.google.com/codelabs/android-navigation#9
Above answers shows how to works with string data types. But if you want to pass custom objects, safe-args also works well;
<fragment
android:id="#+id/verification_fragment"
android:name="com.appname.ui.verification.VerificationFragment"
android:label="VerificationFragment">
<argument
android:name="verificationType"
android:defaultValue="PHONE"
app:argType="com.appname.core.model.VerificationType" />
<deepLink app:uri="android-app://com.appname/verification_fragment?verificationType={verificationType}" />
</fragment>
Pass custom attributes to deeplink;
val request = NavDeepLinkRequest.Builder
.fromUri("android-app://appname/verification_fragment?verificationType=${VerificationType.MAIL}".toUri())
.build()
findNavController().navigate(request)
Read attributes from destination fragments;
private val args: VerificationFragmentArgs by navArgs()
args.verificationType
I have a drawer in my app and I switch fragments by navigation. I would like to have animation transition when I click on the button in the drawer. I know how to do it with regular button but I do not konw how to do it with navigation drawer Thank you.
this is my main Activity
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
appBarConfiguration = AppBarConfiguration(setOf(
R.id.nav_food_text_analysis,
R.id.nav_recipe_analysis
), drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}
this is my navigation xml
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mobile_navigation"
app:startDestination="#id/nav_food_text_analysis">
<fragment
android:id="#+id/nav_food_text_analysis"
android:name="com.example.nutritionfacts.ui.foodTextAnalysis.FoodAnalysisFragment"
android:label="Food analysis"
tools:layout="#layout/fragment_food_text_analysis" >
<action
android:id="#+id/action_nav_food_text_analysis_to_nav_recipe_analysis"
app:destination="#id/nav_recipe_analysis"
app:enterAnim="#anim/enter_from_right"
app:exitAnim="#anim/exit_to_right"
app:popEnterAnim="#anim/enter_from_right"
app:popExitAnim="#anim/exit_to_right" />
</fragment>
<fragment
android:id="#+id/nav_recipe_analysis"
android:name="com.example.nutritionfacts.ui.recipeAnalysis.RecipeAnalysisFragment"
android:label="Recipe analysis"
tools:layout="#layout/fragment_recipe_analysis" >
<action
android:id="#+id/action_nav_recipe_analysis_to_nav_food_text_analysis2"
app:destination="#id/nav_food_text_analysis"
app:enterAnim="#anim/enter_from_right"
app:exitAnim="#anim/exit_to_right"
app:popEnterAnim="#anim/enter_from_right"
app:popExitAnim="#anim/exit_to_right" />
</fragment>
</navigation>
I was just working on this and have a solution that listens for the navigation drawer clicks and reacts in the same way you would with a button.
I am using a single activity with several fragments. Make sure to set up the navigtaion graph for your fragments, then call the correct FragmentDirections action by using the setNavigationItemSelectedListener in the Main Activity.
In my example, the settingsFragment will be loaded with an animation but the aboutFragment will not since it doesn't call a Directions.action function.
MainActivity (inside onCreate function)
/** Handle clicks in Nav Drawer **/
val navigationView = findViewById<NavigationView>(R.id.navView)
navigationView.setNavigationItemSelectedListener { menuItem ->
Timber.d("Nav Drawer Item Clicked: %s", menuItem.title)
when (menuItem.itemId) {
R.id.settingsFragment -> {
navController.navigate(
RecipeListFragmentDirections
.actionRecipeListToSettingsFragment()
)
}
R.id.aboutFragment -> {
navController.navigate(R.id.aboutFragment)
}
R.id.refreshRecipes -> {
Timber.i("TODO: Implement repository refresh method call")
}
}
true
}
navigation.xml
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/navigation"
app:startDestination="#id/recipeList">
<fragment
android:id="#+id/recipeList"
android:name="com.jumptuck.recipebrowser2.recipelist.RecipeListFragment"
tools:layout="#layout/fragment_recipe_list">
<action
android:id="#+id/action_recipeList_to_singleRecipeFragment"
app:destination="#id/singleRecipeFragment"
app:enterAnim="#anim/slide_in_right"
app:exitAnim="#anim/slide_out_right"
app:popEnterAnim="#anim/slide_in_left"
app:popExitAnim="#anim/slide_out_left" />
<action
android:id="#+id/action_recipeList_to_aboutFragment"
app:destination="#id/aboutFragment"
app:enterAnim="#anim/slide_in_left"
app:exitAnim="#anim/slide_out_left"
app:popEnterAnim="#anim/slide_in_right"
app:popExitAnim="#anim/slide_out_right" />
<action
android:id="#+id/action_recipeList_to_settingsFragment"
app:destination="#id/settingsFragment"
app:enterAnim="#anim/slide_in_right"
app:exitAnim="#anim/slide_out_right"
app:popEnterAnim="#anim/slide_in_left"
app:popExitAnim="#anim/slide_out_left" />
</fragment>
One of the reasons I've used this approach is because I want to start a service from the refreshRecipes menu item rather than causing a navigation and this intercepts the click. One of the drawbacks is that the navigation bar items themselves don't show an animation when clicked (however I think this can be fixed).
I have implemented navigation Drawer with Navigation Components in Android. I have 5 fragments that I want to go back to my HomeFragment when I click on back pressed. For the moment they stay onBackStack and do not go to my desired fragment but go to whatever fragment was first.
This is my nav_graph :
<?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"
xmlns:tools="http://schemas.android.com/tools"
app:startDestination="#id/setupFragment"
android:id="#+id/treasure_nav"
android:label="Pick a country">
<fragment android:id="#+id/homeFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.home.HomeFragment"
android:label="Home"
tools:layout="#layout/fragment_home">
<action android:id="#+id/action_home_fragment_to_namesFragment2"
app:popUpTo="#id/homeFragment"
app:destination="#id/namesFragment"/>
<action android:id="#+id/action_home_fragment_to_quranFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/quranFragment"/>
<action android:id="#+id/action_homeFragment_to_tasbeehFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/tasbeehFragment"/>
<action android:id="#+id/action_homeFragment_to_galleryFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/galleryFragment"/>
<action android:id="#+id/action_homeFragment_to_newsFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/newsFragment"/>
<action android:id="#+id/action_homeFragment_to_settingsFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/settingsFragment"/>
</fragment>
<fragment
android:id="#+id/namesFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.names.NamesFragment"
android:label="Names of Allah"
tools:layout="#layout/fragment_names"/>
<fragment
android:id="#+id/quranFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.quran.QuranFragment"
android:label="Quran"
tools:layout="#layout/fragment_quran"/>
<fragment android:id="#+id/tasbeehFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.tasbeeh.TasbeehFragment"
android:label="Tasbeeh"
tools:layout="#layout/fragment_tasbeeh"/>
<fragment android:id="#+id/galleryFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.gallery.GalleryFragment"
android:label="Gallery"
tools:layout="#layout/fragment_gallery"/>
<fragment android:id="#+id/newsFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.news.NewsFragment"
android:label="News"
tools:layout="#layout/fragment_news"/>
<fragment android:id="#+id/settingsFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.settings.SettingsFragment"
android:label="Settings"
tools:layout="#layout/fragment_settings"/>
<fragment android:id="#+id/setupFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.setup.SetupFragment"
android:label="Pick country"
tools:layout="#layout/fragment_setup">
<action android:id="#+id/action_setupFragment_to_homeFragment3"
app:destination="#+id/homeFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/treasure_nav"
app:popUpToInclusive="true"/>
</fragment>
</navigation>
And this is my onBackPressed in my MainActivity (and the only one) :
override fun onBackPressed() {
if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
drawer_layout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
Edit: When i remove the super.onBackPressed() and replace it with :
findNavController(R.id.nav_host_fragment).popBackStack(R.id.homeFragment, false) I achieve what I want. The only problem is that when I am in the homeFragment I want to end the app but I can't.
If my understanding is correct, you want to go back to HomeFragment wherever you are in the navigation flow. For this case you could try registering OnBackPressedCallback on your Fragments via addOnBackPressedCallback, and call popBackStack to navigate to your HomeFragment. Try adding this to Fragments' onViewCreated that need to go back to HomeFragment on backpress:
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
navController = Navigation.findNavController(view);
requireActivity().addOnBackPressedCallback(getViewLifecycleOwner(), () -> {
navController.popBackStack(R.id.homeFragment, false);
});
return true;
});
If you want to close app when press back in HomeFragment, it's just specified these attributes of the last action that navigates you to this destination:
app:popUpToInclusive to true
app:popUpTo to of the last fragment(SetupFragment) that navigates you here(HomeFragment)
It means change your code like this:
<fragment android:id="#+id/setupFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.setup.SetupFragment"
android:label="Pick country"
tools:layout="#layout/fragment_setup">
<action android:id="#+id/action_setupFragment_to_homeFragment3"
app:destination="#+id/homeFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/setupFragment" // this line changes
app:popUpToInclusive="true" /> // and this requires too
</fragment>
I have main navigation:
SplashFragment -> RegistrationFragment -> RootFragment
<fragment
android:id="#+id/splashFragment"
android:name="com.low6.low6.features.splash.SplashFragment"
android:label="Splash"
tools:layout="#layout/fragment_splash" >
<action
android:id="#+id/action_next"
app:clearTask="true"
app:destination="#id/registrationFragment" />
</fragment>
<fragment
android:id="#+id/registrationFragment"
android:name="com.low6.low6.features.registration.RegistrationFragment"
android:label="Register">
<action
android:id="#+id/action_next"
app:clearTask="true"
app:destination="#id/rootFragment" />
</fragment>
<fragment
android:id="#+id/rootFragment"
android:name="com.low6.low6.core.RootFragment"
android:label="#string/home"
tools:layout="#layout/fragment_root" />
And I have nested registration navigation:
RegistrationPersonalFragment -> RegistrationContactFragment -> RegistrationSecurityFragment
<fragment
android:id="#+id/registrationPersonalFragment"
android:name="com.low6.low6.features.registration.RegistrationPersonalFragment"
android:label="Register">
<action
android:id="#+id/action_next"
app:destination="#+id/registrationContactFragment" />
</fragment>
<fragment
android:id="#+id/registrationContactFragment"
android:name="com.low6.low6.features.registration.RegistrationContactFragment"
android:label="Register">
<action
android:id="#+id/action_next"
app:destination="#+id/registrationSecurityFragment" />
</fragment>
<fragment
android:id="#+id/registrationSecurityFragment"
android:name="com.low6.low6.features.registration.RegistrationSecurityFragment"
android:label="Register">
<action
android:id="#+id/action_next"
app:destination="#+id/rootFragment" />
</fragment>
How to redirect from the last nested RegistrationSecurityFragment to RootFragment using Jetpack Navigation component?
Currently
<action
android:id="#+id/action_next"
app:destination="#+id/rootFragment" />
And
navigateTo(R.id.action_next)
Gives me
java.lang.IllegalArgumentException: navigation destination com.xxx:id/rootFragment referenced from action com.xxx:id/action_next is unknown to this NavController
at androidx.navigation.NavController.navigate(NavController.java:691)
at androidx.navigation.NavController.navigate(NavController.java:648)
at androidx.navigation.NavController.navigate(NavController.java:634)
at com.xxx.core.BaseFragment.navigateTo(BaseFragment.kt:73)
at com.xxx.core.BaseFragment.navigateTo$default(BaseFragment.kt:66)
at com.xxx.features.registration.RegistrationSecurityFragment$epoxyController$1$$special$$inlined$button$lambda$1.onClick(RegistrationSecurityFragment.kt:106)
at android.view.View.performClick(View.java:6597)
at android.view.View.performClickInternal(View.java:6574)
at android.view.View.access$3100(View.java:778)
at android.view.View$PerformClick.run(View.java:25885)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
If you have more than one navigation graph, please make sure you're using the right navigation controller. Using Navigation.findNavController(view) in some cases you might need to get your root view to get the root's navigation. Hope, this'll help.
When you have nested NavControllers, findNavController() return only last. To get previous navControllers, you can traverse up using parentFragment property.
Extensions with this approach:
// find all nav controllers from closest to farest
fun Fragment.findAllNavControllers(): List<NavController> {
val navControllers = mutableListOf<NavController>()
var parent = parentFragment
while (parent != null) {
if (parent is NavHostFragment) {
navControllers.add(parent.navController)
}
parent = parent.parentFragment
}
return navControllers
}
// find one nav controller by fragment id
fun Fragment.findNavControllerById(#IdRes id: Int): NavController {
var parent = parentFragment
while (parent != null) {
if (parent is NavHostFragment && parent.id == id) {
return parent.navController
}
parent = parent.parentFragment
}
throw RuntimeException("NavController with specified id not found")
}
And usage:
findAllNavControllers()[2]
findNavControllerById(R.id.navHostFragment)
In your code, you can pass the resource ID of the global action to the navigate() method for each UI element.
your_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Navigation.findNavController(view).navigate(R.id.main_fragment);
}
});
Thanks to #Vladimir answer I came up with this solution
val mainNavView = requireActivity().findViewById<View>(R.id.mainNavFragment)
Navigation.findNavController(mainNavView).navigate(R.id.action_next)
You can do it like this ;
in Child Fragment
// parentFragment = getParentFragment() for java
(parentFragment as MyParentFragment).myNavigationHandler(myArguments)
in Parent Fragment
fun myNavigationHandler(myArguments) {
Navigation.findNavController(binding.root)
.navigate(MyFragmentDirections.actionMyAction(myArguments))
}
findNavController().navigate(HostFragmentDirections.actionHostToOtherFragment())
HostFragment is just an example, which should be the name of host of your nested fragment.
For everyone struggling with navigating to somewhere from your nested fragment, this works.
I am using Navigation from Android Jetpack to navigate between screens.
Now I want to set startDestination dynamically.
I have an Activity named MainActivity
And two Fragments, FragmentA & FragmentB.
var isAllSetUp : Boolean = // It is dynamic and I’m getting this from Preferences.
If(isAllSetUp)
{
// show FragmentA
}
else
{
//show FragmentB
}
I want to set above flow using Navigation Architecture Component. Currently I have used startDestionation as below but it’s not fulfilling my requirement.
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/lrf_navigation"
app:startDestination="#id/fragmentA">
<fragment
android:id="#+id/fragmentA"
android:name="com.mindinventory.FragmentA"
android:label="fragment_a"
tools:layout="#layout/fragment_a" />
</navigation>
Is it possible to set startDestination conditionally using Android Navigation Architecture Component?
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
Java code
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.home_nav_fragment); // Hostfragment
NavInflater inflater = navHostFragment.getNavController().getNavInflater();
NavGraph graph = inflater.inflate(R.navigation.nav_main);
//graph.addArgument("argument", NavArgument)
graph.setStartDestination(R.id.fragment1);
navHostFragment.getNavController().setGraph(graph);
navHostFragment.getNavController().getGraph().setDefaultArguments(getIntent().getExtras());
NavigationView navigationView = findViewById(R.id.navigationView);
NavigationUI.setupWithNavController(navigationView, navHostFragment.getNavController());
Additional Info
As #artnest suggested, remove the app:navGraph attribute from the layout. It would look something like this after removal
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/home_nav_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true" />
</FrameLayout>
In the case of a fragment tag used instead of FragmentContainerView, the above changes remain the same
Some of the APIs have changed, are unavailable or are not necessary since Akash's answer. It's a bit simpler now.
MainActivity.java:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NavHostFragment navHost = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_main_nav_host);
NavController navController = navHost.getNavController();
NavInflater navInflater = navController.getNavInflater();
NavGraph graph = navInflater.inflate(R.navigation.navigation_main);
if (false) {
graph.setStartDestination(R.id.oneFragment);
} else {
graph.setStartDestination(R.id.twoFragment);
}
navController.setGraph(graph);
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!-- Following line omitted inside <fragment> -->
<!-- app:navGraph="#navigation/navigation_main" -->
<fragment
android:id="#+id/fragment_main_nav_host"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="androidx.navigation.fragment.NavHostFragment"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
navigation_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<!-- Following line omitted inside <navigation>-->
<!-- app:startDestination="#id/oneFragment" -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/navigation_main"
>
<fragment
android:id="#+id/oneFragment"
android:name="com.apponymous.apponymous.OneFragment"
android:label="fragment_one"
tools:layout="#layout/fragment_one"/>
<fragment
android:id="#+id/twoFragment"
android:name="com.apponymous.apponymous.TwoFragment"
android:label="fragment_two"
tools:layout="#layout/fragment_two"/>
</navigation>
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"
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/lrf_navigation"
app:startDestination="#id/fragmentA">
<fragment
android:id="#+id/fragmentA"
android:name="com.mindinventory.FragmentA"
android:label="fragment_a"
tools:layout="#layout/fragment_a">
<action android:id="#+id/action_a_to_b"
app:destination="#id/fragmentB"
app:popUpToInclusive="true"
app:popUpTo="#id/fragmentA"/>
<fragment>
<fragment
android:id="#+id/fragmentB"
android:name="com.mindinventory.FragmentB"
android:label="fragment_b"
tools:layout="#layout/fragment_b"/>
</navigation>
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.
if(!isAllSetUp)
{
// FragmentB
navController.navigate(R.id.action_a_to_b)
}
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
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)