Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IllegalArgumentException: navigation destination xxx is unknown to this NavController

In my case, if the user clicks the same view twice very very quickly, this crash will occur. So you need to implement some sort of logic to prevent multiple quick clicks... Which is very annoying, but it appears to be necessary.

You can read up more on preventing this here: Android Preventing Double Click On A Button

Edit 3/19/2019: Just to clarify a bit further, this crash is not exclusively reproducible by just "clicking the same view twice very very quickly". Alternatively, you can just use two fingers and click two (or more) views at the same time, where each view has their own navigation that they would perform. This is especially easy to do when you have a list of items. The above info on multiple click prevention will handle this case.

Edit 4/16/2020: Just in case you're not terribly interested in reading through that Stack Overflow post above, I'm including my own (Kotlin) solution that I've been using for a long time now.

OnSingleClickListener.kt

class OnSingleClickListener : View.OnClickListener {

    private val onClickListener: View.OnClickListener

    constructor(listener: View.OnClickListener) {
        onClickListener = listener
    }

    constructor(listener: (View) -> Unit) {
        onClickListener = View.OnClickListener { listener.invoke(it) }
    }

    override fun onClick(v: View) {
        val currentTimeMillis = System.currentTimeMillis()

        if (currentTimeMillis >= previousClickTimeMillis + DELAY_MILLIS) {
            previousClickTimeMillis = currentTimeMillis
            onClickListener.onClick(v)
        }
    }

    companion object {
        // Tweak this value as you see fit. In my personal testing this
        // seems to be good, but you may want to try on some different
        // devices and make sure you can't produce any crashes.
        private const val DELAY_MILLIS = 200L

        private var previousClickTimeMillis = 0L
    }

}

ViewExt.kt

fun View.setOnSingleClickListener(l: View.OnClickListener) {
    setOnClickListener(OnSingleClickListener(l))
}

fun View.setOnSingleClickListener(l: (View) -> Unit) {
    setOnClickListener(OnSingleClickListener(l))
}

HomeFragment.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    settingsButton.setOnSingleClickListener {
        // navigation call here
    }
}

Check currentDestination before calling navigate might be helpful.

For example, if you have two fragment destinations on the navigation graph fragmentA and fragmentB, and there is only one action from fragmentA to fragmentB. calling navigate(R.id.action_fragmentA_to_fragmentB) will result in IllegalArgumentException when you were already on fragmentB. Therefor you should always check the currentDestination before navigating.

if (navController.currentDestination?.id == R.id.fragmentA) {
    navController.navigate(R.id.action_fragmentA_to_fragmentB)
}

You can check requested action in current destination of navigation controller.

UPDATE added usage of global actions for safe navigation.

fun NavController.navigateSafe(
        @IdRes resId: Int,
        args: Bundle? = null,
        navOptions: NavOptions? = null,
        navExtras: Navigator.Extras? = null
) {
    val action = currentDestination?.getAction(resId) ?: graph.getAction(resId)
    if (action != null && currentDestination?.id != action.destinationId) {
        navigate(resId, args, navOptions, navExtras)
    }
}

What I did to prevent the crash is the following:

I have a BaseFragment, in there I've added this fun to ensure that the destination is known by the currentDestination:

fun navigate(destination: NavDirections) = with(findNavController()) {
    currentDestination?.getAction(destination.actionId)
        ?.let { navigate(destination) }
}

Worth noting that I'm using the SafeArgs plugin.


It could also happen if you have a Fragment A with a ViewPager of Fragments B And you try to navigate from B to C

Since in the ViewPager the fragments are not a destination of A, your graph wouldn't know you are on B.

A solution can be to use ADirections in B to navigate to C


In my case I was using a custom back button for navigating up. I called onBackPressed() in stead of the following code

findNavController(R.id.navigation_host_fragment).navigateUp()

This caused the IllegalArgumentException to occur. After I changed it to use the navigateUp() method in stead, I didn't have a crash again.


Try that

  1. Create this extension function (or normal function):

UPDATED (without reflection and more readable)

import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.NavDirections
import androidx.navigation.fragment.FragmentNavigator

fun Fragment.safeNavigateFromNavController(directions: NavDirections) {
    val navController = findNavController()
    val destination = navController.currentDestination as FragmentNavigator.Destination
    if (javaClass.name == destination.className) {
        navController.navigate(directions)
    }
}

OLD (with reflection)

import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.NavDirections
import androidx.navigation.fragment.FragmentNavigator

inline fun <reified T : Fragment> NavController.safeNavigate(directions: NavDirections) {
    val destination = this.currentDestination as FragmentNavigator.Destination
    if (T::class.java.name == destination.className) {
        navigate(directions)
    }
}
  1. And use like this from your Fragment:
val direction = FragmentOneDirections.actionFragmentOneToFragmentTwo()
// new usage
safeNavigateFromNavController(direction)

// old usage
// findNavController().safeNavigate<FragmentOne>(action)

My problem was

I have a fragment (FragmentOne) that goes to two others fragments (FragmentTwo and FragmentThree). In some low devices, the user press button that redirects to FragmentTwo but in few milliseconds after the user press button that redirects to FragmentThree. The results is:

Fatal Exception: java.lang.IllegalArgumentException Navigation action/destination action_fragmentOne_to_fragmentTwo cannot be found from the current destination Destination(fragmentThree) class=FragmentThree

My solve was:

I check if the current destination belongs to the current fragment. If true, I execute the navigation acion.

That is all!