I'm integrating Android's Navigation Architecture Components into my app. I ran into some problems with passing data to the start of a fragment from an activity, so I was following this answer: Navigation Architecture Component- Passing argument data to the startDestination.
The logic seems sound to me, but I'm struggling to determine how to actually get the NavHostFragment. Elliot Shrock used this line -
val navHostFragment = navFragment as NavHostFragment
But I haven't found a Java equivalent that works.
I tried getting the fragment of my navHost by using
getSupportFragmentManager().findFragmentById(R.id.[mynavhostid])
but this command is returning null. Any ideas?
I solved this by first inflating the view which contains the NavHost, then using the supportFragmentManager to get the navHost Fragment and pass in my default args.
Hoping that Google's Android team provides a cleaner solution to this in the future.
This thing worked for me
val navHostFragment = nav_host_fragment as NavHostFragment
Note, for some reason the debugger will sometimes return null for a NavHostFragment where the code can actually find it without issue. I have no idea why but it's occupied probably 3 hours of my time, make sure it is in fact null by printing or using the fragment!
Actually, verify that the NavHostFragement id in XML matches up with the one you are refering to in your code.
If you want to inflate NavHost via Fragment then use this code in app.
btnClick.setOnClickListener(view -> {
NavHostFragment.findNavController(getParentFragment())
.navigate(R.id.activity_login_to_homeFragment);
}
on this code ID MUST BE SAME AS NAVIGATION.XML File attachment.
☻♥ Done.
So, attempting to retrieve the NavController in onCreate() of the Activity using Navigation.findNavController(Activity, #IdRes int) will fail. You will have to directly retrieve it from the support fragment manager. First, make sure your activity extends AppCompatActivity. Otherwise, you will not be able to use getSupportFragmentManager(). Then, this code should work:
NavHostFragment navHostFragment =
(NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
NavController navController = navHostFragment.getNavController();
Further reading: https://developer.android.com/guide/navigation/navigation-getting-started
Related
I'm relatively new with Kotlin and I'm trying to extend the navigation drawer example by adding a button into my fragment that can open another fragment directly without having to select it from the navigation drawer.
Here is my button listener in my fragment's onCreateView function:
addButton.setOnClickListener()
{
Toast.makeText(this.context,"ButtonPressed",Toast.LENGTH_SHORT).show()
this.activity.supportFragmentManager.beginTransaction().replace(R.id.mobile_navigation, R.layout.fragment2).commit()
}
I'm not sure I completely understand how to approach this. The button works fine and calls the toast, but I can't get it to change the fragment.
Any help would be appreciated.
the template has a mobile_navigation.xml file with a "navigation" element.
The Navigation Architecture components is used by default in recent android studio versions, so the navController is the responsible for fragment transaction instead of doing that manually by the supportFragmentManager
addButton.setOnClickListener() {
Toast.makeText(this.context,"ButtonPressed",Toast.LENGTH_SHORT).show()
val navHostFragment = requireActivity().supportFragmentManager
.primaryNavigationFragment as NavHostFragment
navHostFragment.navController.navigate(R.layout.fragment2)
}
Also, make sure that R.layout.fragment2 is the fragment id of the destination fragment in the R.id.mobile_navigation.xml
I am using NavController to manages app navigation:
findNavController().navigate(action)
I got a few crashes in Crashlytics: I found it is because:
MyFragment {
...
myLiveData.observer(viewLifecycleOwner, Observer) {
findNavController().navigate(myAction) // currentDestination is null ...
})
...
navController.currentDestination? is an optional, When it is null, app crashes with unhandled exception.
Since currentDestination is declared as optional, I guess there must be some legit reason why it could be null, that I don't know. Appreciate in advance for any pointer.
I was experiencing the same issue.
At seemingly random times, the navigate to my destination fragment would crash due to the currentDestination being null.
Similar to the OP, I was triggering the nav through a Flow (not a live data).
Despite collecting the flow with the viewLifecycleOwner, it almost seemed like the fragment wasn't ready to navigate. What I found that fixed the issue was a little surprising. It was how the previous fragment was "popping" itself.
FragA -> FragB
FragB.popBackStack()
FragA -> VERY Quickly re-nav to FragB (null currentDestination == Crash)
However, as a test, I tried using
FragB.popBackStack(fragA.id, false)
And the crashes stopped. The currentDestination was never null again.
This must be a bug in the navComponent library.
My fix was as follows, and is still working (fingers crossed).
Instead of "findNavController.popBackStack()" I use
findNavController().previousBackStackEntry?.let {
findNavController().popBackStack(it.destination.id, false)
} ?: run {
findNavController().popBackStack()
}
Hope that works for someone else also.
edit Left in for posterity.. but.. I was wrong. this didn't fix it afterall. My mistake. Carry on.
Destination represents the node in the NavGraph that's being hosted by the NavHost. NavController just manages the flow. There are few ocasions when NavHost is not showing any destination e.g.:
before you set the NavGraph (because destination represents position in the graph)
when you manually inflate something in the NavHost using transaction (outside of the graph's scope)
If you have multiple graphs in one app (e.g. nested graphs, but can also be independent) you may have one NavController giving main graph destination and a secondary one returning null, etc.
Thanks Stachu, any relationship to fragment viewLifecycle?
In my case, the navigation is triggered from a liveData observer, i.e.,
MyFragment {
...
myLiveData.observer(viewLifecyucleOwner, Observer) {
findNavController().navigate(myAction) // currentDestination is null ...
}
...
I need to check if an nav graph has deeplink and NavGraph provides hasDeeplink() method that I want to use. But I need this information outside activity or fragment. Is there any way to init NavGraph with navigation id resource outside fragment/activity?
I tried some options to get NavGraph instance but every ends with an exception.
Thanks
I was trying to pass data between fragments while I was passing from upper fragment to down fragment, but I don't want to use the navigate method. It adds a new destination to the process history, but I want back to the previous fragment, restore it data and refresh the view.
Is it possible to do it with NavController and popBackStack method?
After few hours I found a strange solution. findNavController().popBackStack() cannot pass data to the previous fragment, but we can do it using the findNavController().graph.defaultArguments, set the data in one fragment, and get the data in the second one. I believe someone will find the better solution.
I also got same problem and I have used below solution which is working, But i am not sure its correct way or not.
val navOptions = NavOptions.Builder().setPopUpTo(R.id.id_of_fragment_one, true).build()
view?.findNavController()?.navigate(R.id.id_of_fragment_one, bundleOf("id" to model), navOptions)
I have one NavController (navOrdersController) with 2 fragments inside. My current fragment is OrderDetailFragment and I want access to first Fragment (OrdersFragment) to update data in MainActivity. How can I do this?
I tried findFragmentByTag and findFragmentByID but always return null.
I got access OrderDetailFragment with:
this.supportFragmentManager.fragments[position].findNavController().currentDestination
But I need previous fragment. Thank you very much
Can't say the code snippet (I've added below) is the best solution, but it worked for me to get the recent/current fragment added in NavHostFragment used in Android Navigation Component's implementations
OR
You can even override onActivityResult of the you activity with your fragment's, all you need is to get the added Fragment and call it's onActivityResult, using this code snippet:
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
Fragment fragment = navHostFragment.getChildFragmentManager().getFragments().get(0);
fragment.onActivityResult(requestCode, resultCode, data);
If both the fragments have same parent activity, i.e. use getActivity() to get activity reference and use Activity.getSupportFragmentManager to find the fragment.
.....
Activity X -> Fragment A -> Fragment B
If fragment B is child of Fragment A, use getParentFragment. The fragment you are looking for would be this fragment.
Let me know if anything is not clear.
PS: Jetpack Navigation library does the same thing which we have been doing explicit. So, the previous logic of Activity-Fragment is still valid.
EDIT:
try out (parentFragment as NavHostFragment).childFragmentManager.findFragmentById()
I forgot last time that activity has declaration of NavHostFragment in its layout so there will always root parent fragment of type 'NavHostFrament'
EDIT:
I have found a work around for it
parentFragment.childFragmentManager.getFragment(Bundle().also { it.putInt("foobar",0) }, "foobar")