Navigating from BottomSheetFragment with Jetpack Navigation results with crash 'Action can not be found from current destination'

Viewed 208

This case might be a unique one because I do have some complicated navigation logic and I'm using extension functions(which I will share below shortly).

  • Imagine having a Fragment(A) opening a BottomSheetFragment(B)
  • And the BottomSheetFragment(B) has a button inside.
  • Tapping on the button dismisses the BottomSheetFragment(B) and navigates to a new Fragment(C).

To handle this case, I'm using 2 extension functions:

fun <T> Fragment.setNavigationResult(key: String, result: T) {
    findNavController().previousBackStackEntry?.savedStateHandle?.set(key, result)
}

fun <T> Fragment.getNavigationResult(key: String) = findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<T>(key)

Implementation of setNavigationResult in BottomSheetFragment(B)

loginButton.setOnClickListener {
            dismiss()
            setNavigationResult(EVENT_CUSTOMER_SIGNUP_LOGIN, Event(true))
        }

Implementation of getNavigationResult in Fragment(A)

getNavigationResult<Event<Boolean>>(EVENT_CUSTOMER_SIGNUP_LOGIN)?.observe(viewLifecycleOwner) { event ->
            event.value?.let {
                findNavController().navigate(R.id.openCustomerLogin)
            }
        }

The application crashes with the below error:

    java.lang.IllegalArgumentException: Navigation action/destination openFragmentC cannot be found from the current destination Destination(FragmentB)

What I don't understand here is, the current destination is still BottomSheetFragment(B) even I called dismiss before.

When I add a delay before navigating to FragmentC, then the application does not crash.

lifecycleScope.launch {
                    delay(250)
                    findNavController().navigate(R.id.openCustomerLogin)
                }

I don't like the solution with delay, because it's not practical and can easily be forgotten in new implementations.

Sorry for the long post, I will appreciate it if you share your thoughts on this and assist me.

1 Answers

I have implemented like below

suppose below code for bottomsheet while click on button

 private fun closeBottomSheet() {
        findNavController().previousBackStackEntry?.savedStateHandle?.set("refresh", true)
        findNavController().navigateUp()
    }

and below code for fragment A

val observer = LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_RESUME
                && navBackStackEntry.savedStateHandle.contains("refresh")
            ) {
                val result = navBackStackEntry.savedStateHandle.get<Boolean>("refresh")
                if (result!!) {
                    result.run {
                        // whatever you do , go from fragment A to Fragment C
                        navBackStackEntry.savedStateHandle.remove<Boolean>("refresh")
                    }
                }
            }
        }
    

And also check you have define action in fragment A in navigation.xml

Related