23

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.

Gabrielle
  • 4,933
  • 13
  • 62
  • 122

6 Answers6

24

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())
}
Joscplan
  • 1,024
  • 2
  • 15
  • 35
  • 4
    This worked for me. Basically, you are checking if fragment still attached to do so. If not, is because you have navigated already. Thank you very much. – Amg91 Apr 21 '20 at 17:06
  • @Amg91 This solution works but why do we have control in an already Garbage Collected/backPressed fragment? – Ramakrishna Joshi Jun 22 '21 at 17:56
10

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

Mohamed Eleish
  • 171
  • 1
  • 5
0

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)
Biscuit
  • 4,840
  • 4
  • 26
  • 54
0

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.

  1. import androidx.navigation.fragment.findNavController

    This says findNavController()

  2. import androidx.navigation.Navigation.findNavController

    This says findNavController(Activity, Int)

  3. 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.

lets start coding
  • 1,839
  • 1
  • 10
  • 19
0

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 )
-1

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(.....)
Amin Keshavarzian
  • 3,646
  • 1
  • 37
  • 38