I use Jetpack Navigation for navigating between fragments and activity. So, I have a MainActivity with a FragmentContainerView in the layout. I can easily navigate from fragment to fragment/activity. But, I can't find a way how to navigate from one activity to another activity/fragment with navController.
For example, from fragment FA, I call navController.navigate() to activity A. Now, I want to navigate from activity A to activity B or fragment FB.
I already tried this:
val host = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = host.navController
But always got this error
null cannot be cast to non-null type androidx.navigation.fragment.NavHostFragment
Thanks!
A note from the navigation component guide
Note: The Navigation component is designed for apps that have one main activity with multiple fragment destinations. The main activity is associated with a navigation graph and contains a NavHostFragment that is responsible for swapping destinations as needed. In an app with multiple activity destinations, each activity has its own navigation graph.
You shouldn't use the navigation component to navigate from one activity to another. It is made to swap fragments on a Fragment container.
You can get navController inside activity using
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
You also have to add android:name="androidx.navigation.fragment.NavHostFragment" to your fragment container
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
For java coder;
it is possible to get NavController instance this way
NavHostFragment navHostFragment =(NavHostFragment)getSupportFragmentManager()
.findFragmentById(R.id.nav_host_fragment);
NavController navController = navHostFragment.getNavController();
Related
I currently have this nav_graph.xml file.
Activity A (parent container), with this navigation tree:
Fragment A -> Fragment B -> Fragment C -> Fragment D -> Fragment E.
Also, I have an Activity B, and I want to navigate directly to Fragment C from this activity.
Does anyone know how I can realize an elegant solution for this case?
I have thought that I could launch an intent from Activity B -> Activity A passing it as an extra an enum corresponding to the desired fragment and that Activity A would handle the received parameter navigating through an action to the corresponding fragment. How do you see it? I think it can be improved and that's why I'm looking for a second solution. thanks!
Add this (your nav host fragment) in both of your activities with
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_view_pager_host"
android:layout_width="match_parent"
android:layout_height="670dp"
app:layout_constraintBottom_toTopOf="#+id/bottomNav"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="#navigation/main_nav_graph"
app:defaultNavHost="true"/>
and then in your activity B onCreate() just add
val navController = (supportFragmentManager.findFragmentById(R.id.nav_view_pager_host) as NavHostFragment).navController
navController.navigate(/* To your fragment B*/)
//Handle anything else you wanna ddo
I have navigation competent just go from Fragment A to B , however going back from B to A , making A loose everything, text field data, click linstners, there is code inside oncreeteView to init view also does be called , ViewModel live data no more active
<fragment
android:id="#+id/booking_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/appBarLayout"
app:navGraph="#navigation/nav_graph" />
Oncreate
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.booking_nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
val appBarConfiguration = AppBarConfiguration(
topLevelDestinationIds = setOf( R.id.bookingFragment),
fallbackOnNavigateUpListener = ::onSupportNavigateUp
)
toolbar_details.setupWithNavController(navController, appBarConfiguration)
setSupportActionBar(toolbar_details)
supportActionBar?.setDisplayShowTitleEnabled(false)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
In 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"
android:id="#+id/nav_graph"
app:startDestination="#id/a">
<fragment
android:id="#+id/a"
android:name=".A"
android:label=" ">
<action
android:id="#+id/action_a_to_b"
app:destination="#id/b" />
Fragment A :
private val viewModelFilter: filterViewModel by sharedViewModel()
do you have an Activity to hold the fragments or it's just two fragments A and B?
I think the easy way to use one based Main Activity having the fragments on it
and init the main view Model in the Activity and shear it at the fragments so you keep the data alive because its lifecycle ended with the fragment associated with it
And since the viewModel expires and onCleared runs at the end of the life of the fragment from which the information is reduced, so it must be created in the Activity so that it remains running and the fragment is reduced from it.
here is an example code :
1- in main Activity
viewModel=ViewModelProvider(this).get(ViewModel::class.java)
2- in the first fragment you to get the same viewModel instance from the main actitivty
activity.let {
viewModel=ViewModelProvider(it!!).get(ViewModel::class.java)
}
and this way you get the view Model alive throw the fragment's lifecycle and destroyed when the activity is finished
I have this tabbed UI with a navigation component and a BottomNavigationView that handles the tabs.
My use case involves one of these tab fragments to have a BottomNavigationView of its own.
I don't think the nested navigation graphs will work for me because I need there to be an inner NavHostFragment and a second BottomNavigationView.
So I'm in the fragment which I wish to host my inner navigation graph. It contains a fragment like so.
<androidx.fragment.app.FragmentContainerView
android:id="#+id/inner_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
I need a way to get the navigation controller for the above fragment.
Normally when you're in an activity you'd call the findFragmentById from the supportFragmentManager with the id you gave to your fragment.
val navHostFragment = supportFragmentManager.findFragmentById(R.id.outer_nav_host) as NavHostFragment
So I tried to call that on the activity object from within my inner fragment
requireActivity().supportFragmentManager.findFragmentById(R.id.inner_host_fragment)
But the findFragmentById returns null.
Try to get the NavHostFragment from the supportFragmentManager, and then get the navController upon.
val navHostFragment =
requireActivity().supportFragmentManager.primaryNavigationFragment as NavHostFragment
val navController = navHostFragment.navController
Thanks to #ianhanniballake you need to use the childFragmentManager as your current fragment is a child of a fragment.
So try to use instead:
val navHostFragment =
childFragmentManager.primaryNavigationFragment as NavHostFragment
val navController = navHostFragment.navController
UPDATE:
throws this java.lang.NullPointerException: null cannot be cast to non-null type androidx.navigation.fragment.NavHostFragment
So, the solution as from comments:
val navHostFragment = childFragmentManager.findFragmentById(R.id.nav_host_fragment)
as NavHostFragment
And replace nav_host_fragment with the id of the inner NavHostFragment.
I have a fragment that I define a NavHostFragment inside it like this:
<fragment
android:id="#+id/shipping_host_nav"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/shipping_nav_graph"
app:defaultNavHost="true"/>
when trying to call findNavController method in the fragment it threw an illegal state exception and says that my view group doesn't have a NavController.
java.lang.IllegalStateException: View androidx.core.widget.NestedScrollView{1dd5506 VFED..... ......I. 0,0-0,0} does not have a NavController set
So my question is: can I define a NavHostFragment inside another fragment?
or suitable for activity only?
I have searched a lot to find can I define a nav host fragment inside another fragment but I didn't find any answers.
I have found a solution for this exception, the findNavController() throws this exception when trying to call this method within a fragment that is not NavHostFragment or not within NavHostFragment so I made this mistake by calling this method in my fragment.
So I have to find the controller by myself using Navigation class
Navigation.findNavController(activity, R.id.my_nav_host_fragment)
this is how to find the NavHostFragment (NavController) defined within a fragment
I made an extension function for Fragment class so I can be easily find the nav controller using id
fun Fragment.getFragmentNavController(#IdRes id: Int) = activity?.let {
return#let Navigation.findNavController(it, id)
}
I have two graphs, so the first graph move from one fragment to an activity passing safeArgs to the activity.
val action = MyFragmentDirections.actionMyActivity(arg1, arg2)
Navigation.findNavController(view).navigate(action)
Now in the second, I want to pass these arguments from MyActivity to a fragment which belongs to this activity.
I can get the args:
val args = MyActivity.fromBundle(intent.extras)
The problem is there is not a Directions file for this activity, so I can't pass the arguments.
Navigation 1.0.0-alpha07 fixed the feature request for passing arguments to the start destination of a graph.
To use this, you'd need to:
Remove the app:navGraph attribute from your NavHostFragment
Call findNavController(R.id.your_nav_host_fragment).setGraph(R.navigation.your_graph, intent.extras)
Using the R.id of your NavHostFragment and R.navigation that you previously had on your app:navGraph tag. By passing the arguments into the setGraph call, your starting destination will get the arguments directly, without calling navigate again (which would, by default, create a new instance of the destination on your back stack - not what you want).
I don't know if this is recommended, but it is working:
val args = MyActivity.fromBundle(intent.extras)
navController.navigate(R.id.myActivityFragment, args.toBundle())
It work for me
Remove the app:navGraph attribute from your NavHostFragment
<androidx.fragment.app.FragmentContainerView
android:id="#+id/video_view_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="false"
app:defaultNavHost="true"
app:layout_constraintTop_toTopOf="parent"
/>
and call
navHostFragment = supportFragmentManager
.findFragmentById(R.id.video_view_fragment) as NavHostFragment
navHostFragment.navController.setGraph(R.navigation.video_navigation, intent.extras)