Reputation: 4991
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.
Upvotes: 24
Views: 29658
Reputation: 144
In my case this issue was connected to MenuHost. If you use a new MenuHost/MenuProvider in your fragment/s, when you have multiple fragments with their menus, pressing menu item could invoke the item in inactive fragment. :
MenuHost menuHost = requireActivity();
MenuProvider mm = new MenuProvider() {
@Override
public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {
NavController navController = NavHostFragment.findNavController(YourFragment.this);
if(navController.getCurrentDestination().getId()==R.id.yourfragmentid){
menu.clear();
menuInflater.inflate(R.menu.menu_main, menu);
}
}
@Override
public boolean onMenuItemSelected(@NonNull MenuItem item) {
NavController navController = NavHostFragment.findNavController(ReportsFragment.this);
if(navController.getCurrentDestination().getId()==R.id.reports) {
return NavigationUI.onNavDestinationSelected(item, navController);
}else {
return false;
}
}
};
menuHost.addMenuProvider(
mm,
this.getViewLifecycleOwner(),
Lifecycle.State.RESUMED
);
Upvotes: 0
Reputation: 39
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 )
Upvotes: 0
Reputation: 75
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)
Upvotes: 0
Reputation: 2513
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.
Upvotes: 0
Reputation: 3963
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(.....)
Upvotes: -1
Reputation: 331
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
Upvotes: 11
Reputation: 1034
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())
}
Upvotes: 24