Sorry if the naming is bad, but, there are too many things, so had to change the names. But I am having an issue with navigation. I have a nav_graph as following.
<fragment
android:id="#+id/SomeFragment"
android:name="com.akhilsreekar.fragments.SomeFragment"
android:label="fragment_some"
tools:layout="#layout/fragment_some">
<action
android:id="#+id/action_SomeFragment_to_OtherFragment"
app:destination="#id/workerDetailFragment"
app:popUpTo="#+id/QRScanFragment"/>
<!...some other arguments and actions...>
</fragment>
<dialog
android:id="#+id/OtherFragment"
android:name="com.akhilsreekar.fragments.OtherFragment"
android:label="OtherFragment">
<!...some arguments and actions...>
</dialog>
I am navigating using this to show the dialog in my fragment
findNavController().navigate(
QRScanFragmentDirections.actionSomeFragmentToOtherFragment(
*reqArgs*
)
Where class OtherFragment: BottomSheetDialogFragment{...}. OtherFragment is being opened. But the problem is when I navigate to OtherFragment,the onPause function of SomeFragment is not being called. and vice versa( that is when I dismiss the otherfragment, the onResume is not being called in someFragment). What is the reason? Am I missing something?
Related
I have a question. I'm using nav component for navigation. For example i have fragment A, B and C and bottomNavigation. I'm using
binding.bottomNavigation.setupWithNavController(navController)
For multiple backstack. But here is situation: Main frag is A. I'm moving to fragment B or C. I have buttons on fragments B and C which should lead me to fragment A with putted arguments in it so i'm using just:
findNavController().navigate(fragmentBDirections.fromFragmentBToFragmentA(argument))
But here is a problem. I'm recreating fragment A after this but i'm already have this fragment in backstack. So is it possible to find A in backstack and navigate to it without recreating? Is it possible to save backstack after that?
Your problem seems like an ideal case to use a sharedViewModel
Your button in B or C should pop and fallback to A after updating a property in the viewModel. On leaving, the viewModel is not destroyed because it is bound to the activity and is available for Fragment A.
Bonus is using LiveData so that the change is observed and updated automatically
I think SavedStateHandle might be helpful.
<Navigation>
<fragment
android:id="#+id/BFragment"
android:name="com.packageName.app.BFragment"
android:label="fragment_b"
tools:layout="#layout/fragment_b" >
<action
android:id="#+id/action_BFragment_pop_including_AFragment"
app:popUpTo="#id/AFragment"
app:launchSingleTop="true" />
</Navigation>
<Navigation>
<fragment
android:id="#+id/CFragment"
android:name="com.packageName.app.CFragment"
android:label="fragment_c"
tools:layout="#layout/fragment_c" >
<action
android:id="#+id/action_CFragment_pop_including_AFragment"
app:popUpTo="#id/AFragment"
app:launchSingleTop="true" />
</Navigation>
try it . it's my solution
Here is my code
<fragment
android:id="#+id/fragment1"
android:name="com.example.app.Fragment1"
android:label="SignatureFragment"
tools:layout="#layout/layout_fragment1">
<action
android:id="#+id/action_fragment1_to_main_activity"
app:destination="#id/main_activity"
app:enterAnim="#anim/slide_in_from_right"
app:exitAnim="#anim/no_anim"
app:launchSingleTop="true"
app:popEnterAnim="#anim/no_anim"
app:popExitAnim="#anim/slide_out_to_right"
app:popUpTo="#id/navigation_graph_id"
app:popUpToInclusive="true" />
</fragment>
<activity
android:id="#+id/main_activity"
android:name="com.example.app.MainActivity"
android:label="MainActivity"
tools:layout="#layout/activity_main" />
Now the code for navigation
findNavController().navigate(R.id.action_fragment1_to_main_activity)
When I navigate to activity and press back, the fragment is still there. I want to clear the backstack after opening the activity.
I tried to remove the animation and also tried with removing app:launchSingleTop, but no success.
Edit
Jetpack Navigation is intended to work with single activity and does not fully support activity navigation with parameters passed to actions
Thus to clear stack when navigating from one activity to another you will still need to call activity.finish()
Edit end
The thing is findNavController().navigate(R.id.action_fragment1_to_main_activity) wont work.
Try to navigate via navigate(#NonNull NavDirections directions). In your case it will look something like this
findNavController().navigate(
Fragment1Directions.actionFragment1ToMainActivity())
Hope it helps.
I started using navigation component in my application and I am facing with the following problem.
My first fragment is LoginFragment. After a success login, the mainFragment is displayed. I want that when user is on mainFragment and press back button to not go back to loginFragment. For this I added these 2 lines in nav_graph : app:popUpTo="#+id/lovable_app_navigation" and app:popUpToInclusive="true" and it works well. Here is my navigation 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/app_navigation"
app:startDestination="#id/loginFragment">
<fragment
android:id="#+id/loginFragment"
android:name="com.xxx.LoginFragment"
android:label="LoginFragment"
tools:layout="#layout/login_fragment">
<action
android:id="#+id/dashboard_action"
app:destination="#id/mainFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/app_navigation"
app:popUpToInclusive="true"/>
</fragment>
<fragment
android:id="#+id/mainFragment"
android:name="com.xxx.MainFragment"
android:label="MainFragment"
tools:layout="#layout/main_fragment">
<action
android:id="#+id/logout_action"
app:destination="#id/loginFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/app_navigation"
app:popUpToInclusive="true"/>
</fragment>
<action
android:id="#+id/action_global_loginFragment"
app:destination="#id/loginFragment" />
</navigation>
The problem is that after a time, when my session expires, it doesn't matter where the user is in application, in which fragment, I must to display the LoginFragment over the all stack. I created a global action for this action_global_loginFragment. The problem is that when I navigate to LoginFragment I get this error :
java.lang.IllegalStateException: Fragment LoginFragment{1d6bd24 (829a6832-3480-4bcb-a3f6-7e2ba214d3ca)} not associated with a fragment manager.
If I remove popUpTo and popUpToInclusive it works fine, but then the back button functionality is affected, from mainFragment it goes back to loginFragment.
Any idea how to fixed this?
Thanks in advance.
The same issue was happening to me, I managed to solve it by wrapping the navigate method inside a view?.post call like so:
view?.post {
findNavController().navigate(SplashFragmentDirections.actionSplashFragmentToLoginFragment())
}
The problem is caused when you try to call findNavController() on a fragment that has been detached, if you're using Kotlin you can create an extension function like so
fun Fragment.findNavControllerSafely(): NavController? {
return if (isAdded) {
findNavController()
} else {
null
}
}
then use it in any fragment
findNavControllerSafely()?.navigate(/*pass here the nav directions>*/)
you can also surround it with try/catch but I do not recommend this as it will silently catch/ignore other exceptions that might be useful, try navigating through the source code of findNavController() to get a better feel of the kind of exceptions that are thrown
for me the problem was the fragment after restoration because of config change was the fragment manager was not valid in fragment so I had to get it directly from activities like this:
activity?.findNavController(R.id.nav_host_fragment)?.navigate(....)
or
activity?.supportFragmentManager?.setFragmentResultListener(.....)
I was facing the same issue today.
the problem was with the importing of the wrong file.
there are actually 3 files that can be imported.
import androidx.navigation.fragment.findNavController
This says findNavController()
import androidx.navigation.Navigation.findNavController
This says findNavController(Activity, Int)
import androidx.navigation.fragment.NavHostFragment.findNavController
This says findNavController(Fragment)
If I used 2nd one it gave an error Fragment not attached/associated with/to an Activity. (don't remember the exact error)
If I used 3rd one I was getting the same error as #Gabrielle
so, for me, the first one worked perfectly fine since it doesn't require either Activity or Fragment.
I've had this issue too and its very confusing to know whats wrong!
I found this URL That helped me a lot!:Android Navigation Component Issue
This guy suggest to create a function on every fragment, due to when using the findNavController() with a fragment that isn't NavHostFragment or isn't within NavHostFragment this exception will be thrown.
fun Fragment.getFragmentNavController(#IdRes id: Int) = activity?.let {
return#let Navigation.findNavController(it, id)
}
Then, you can all your navigation instance with the id just like this:
getFragmentNavController(R.id.shipping_host_nav).navigate(R.id.id_of_host_nav)
im cereate an extention for this issue , it work based on fragments lifecycle and kotlin
fun NavController.lifeCycleNavigate(lifecycle :LifecycleCoroutineScope, resId :Int) =
lifecycle.launchWhenResumed {
navigate(resId)
}
now we can use it simply with navController , like this
findNavController().lifeCycleNavigate(lifecycleScope , R.id.destination )
I have a graph defined using XML and I added a DialogFragment as one of the Fragment in my NavGraph. when I call NavController.navigate with resId and bundle on that nav controller I don't see any Dialog being displayed. Is there any way I can use DialogFragment instead of standard Fragment?
<fragment
android:id="#+id/noLoginDialog"
android:name="com.ram.view.NoLoginDialog"
android:label="NoLoginDialog">
<argument
android:name="argTitle"
android:defaultValue="null"
app:type="string"/>
<argument
android:name="argBody"
android:defaultValue="null"
app:type="string"/>
<argument
android:name="argButton"
android:defaultValue="null"
app:type="string"/>
</fragment>
and my action is defined as below
<container_fragment
android:id="#+id/homeFragment"
android:name="com.ram.home.HomeFragment"
android:label="HomeFragment">
<action
android:id="#+id/action_homeFragment_to_noAuthAlertDialog"
app:destination="#id/noLoginDialog"/>
</container_fragment>
My other actions with activity and fragments works just fine.
hum, Navigation Library will never open an Fragment as Dialog, it just replace the first (home destination) to other destination in your NavHostFragment.
Google said:
A destination is any place you can navigate to in your app. While
destinations are usually Fragments representing specific screens..
Please provide more information, like your navigation code (java/kotlin).
Take a read on this official Google post to understand more about Navigation:
https://developer.android.com/topic/libraries/architecture/navigation/navigation-implementing
Yes you can call up DialogFragment from navigation, you will just have to explicitly tell the navigation that you are going to use dialog
<dialog
android:id="#+id/simCancelFinish"
android:name="mk.telekom.kiosk.ui.dialogs.SimCancelFinish"
android:label="SimCancelFinish" >
<action
android:id="#+id/action_simCancelFinish_to_stornoSimFragment"
app:destination="#id/stornoSimFragment"
app:popUpTo="#id/stornoSimFragment"
app:popUpToInclusive="true" />
</dialog>
I am trying out the new Navigation Architecture Component, and I can't figure out how to do this:
I have 1 Activity (MainActivity) + 3 Fragments:
SplashFragment (Home)
MainFragment
SignUpFragment
I would like to use SplashFragment to determine if I should navigate to MainFragment or SignUpFragment, but once it reaches either of those 2, you should not be able to pop back to SplashFragment. How can I do that with the new navigation component?
I tried popBackStack before and after calling navigate(R.id.action_xxx), but neither of them work (which make sense: before it has nothing to pop; after it just closes the fragment that just got added). Does that mean the only way to do that is to override onBackPress to intercept it and make sure navigateUp does not get call in those cases?
Thanks!
First, add attributes app:popUpTo='your_nav_graph_id' and app:popUpToInclusive="true" to the action tag.
<fragment
android:id="#+id/signInFragment"
android:name="com.glee.incog2.android.fragment.SignInFragment"
android:label="fragment_sign_in"
tools:layout="#layout/fragment_sign_in" >
<action
android:id="#+id/action_signInFragment_to_usersFragment"
app:destination="#id/usersFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/main_nav_graph"
app:popUpToInclusive="true" />
</fragment>
Second, navigate to the destination, using the above action as parameter.
findNavController(fragment).navigate(SignInFragmentDirections.actionSignInFragmentToUserNameFragment())
NOTE: If you navigate using method navigate(#IdRes int resId), you won't get the desired result. Hence, I used method navigate(#NonNull NavDirections directions).
This worked for me in alpha05 release. Add app:popUpTo="#id/nav_graph" in the action tag(inside your nav_graph.xml file).
Here "#id/nav_graph is the id of my graph or also called as the Root.
<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/startFragment">
.......
<action
android:id="#+id/action_startFragment_to_homeFragment"
app:destination="#id/homeFragment"
app:popUpTo="#id/nav_graph"/>
.......
You can also do this in design tab:- select "SplashFragment" and select the action you want to change and then pick "root" for "Pop To"
WARNING: clearTask has been deprecated and will be remove in future release, not sure what the solution is. Please follow this issue for now to keep up to date
Oh after 10 minutes finally found the key: use clearTask.
All I have to do is add app:clearTask="true" to that specific action, or use .navigate(R.id.actionXXXX, null, NavOptions.Builder().setClearTask(true).build()), and it's done. Just make sure you add it to all the children of SplashFragment (in this case, both MainFragment and SignUpFragment).
So if you have splash fragment and main fragment and you don't want to go back to splash fragment after the main fragment below method you can achieve this
<fragment
android:id="#+id/splashFragment"
android:name="com.example.youappname.views.SplashFragment"
android:label="fragment_splash"
tools:layout="#layout/fragment_splash">
<action
android:id="#+id/action_splashFragment_to_mainFragment"
app:destination="#id/mainFragment"
app:popUpTo="#id/splashFragment"
app:popUpToInclusive="true"/>
</fragment>
In you Kotlin Splash Fragment:
private lateinit var navController: NavController
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navController = Navigation.findNavController(view)
}
private fun navigateToMainFrag() {
navController.navigate(R.id.action_splashFragment_to_mainFragment)
}
Now when you press back button it will close the app instead of showing the splash screen
For anyone wanted to do this purely in code:
Navigation.findNavController(v)
.navigate(R.id.action_splashFragment_to_userProfileFragment2, null,
new NavOptions.Builder().setPopUpTo(R.id.splashFragment, true).build())
The sample solution is add a onBackPressedDispatcher on Owner Activity of fragment/navigation:
https://developer.android.com/guide/navigation/navigation-custom-back#implement_custom_back_navigation