I have Fragment A, BottomSheetDialogFragment B, and DialogFragment C. I want to be able to choose an action from B, dismiss B to go back to A, then use the navigation livedata to navigate from A to C.
A and B shares the same activityViewModel. My intention is that when the action button in B is clicked, it will dismiss B, modify a flag in the activityViewModel, and A will observe change to the flag and open C subsequently. However, I get an error and I think that it think I'm navigating from B to C (instead of A to C):
java.lang.IllegalArgumentException: navigation destination ...action_FragmentA_to_DialogFragmentC is unknown to this NavController
I believe the exception is due to program thinks the action is instead from B to C.
class DetailBottomSheet : BottomSheetDialogFragment() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private val detailViewModel by activityViewModels<DetailViewModel>() { viewModelFactory }
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding.bottomSheetAction.setOnClickListener {
dismiss()
detailViewModel.setBottomSheetDialogType(1)
}
}
}
class DetailFragment : Fragment() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private val detailViewModel by activityViewModels<DetailViewModel>() { viewModelFactory }
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
detailViewModel.bottomSheetDialogType.observe(viewLifecycleOwner, Observer {
when (it) {
1 -> detailViewModel.navigateToDialogFragmentC(args.id)
else -> {}
}
})
detailViewModel.navigateToDialogFragmentC.observe(
viewLifecycleOwner,
WrapperEventObserver {
Thread.sleep(500) // thought this would allow wait time for bottomsheet to dismiss, but doesn't make a difference
val action =
DetailFragmentDirections.actionFragmentAToDialogFragmentC(it)
findNavController().navigate(action)
})
}
}
What fixes are available to me navigate properly in this situation?
Calling dismiss()
on any DialogFragment
asynchronously updates the NavController
state - this means that from NavController
's perspective, you are still on DetailBottomSheet
when your bottomSheetDialogType
Observer fires.
You should instead use findNavController().popBackStack()
, which will synchronously update the NavController
's state (effectively moving you back to A) and calling dismiss()
itself to hide the bottom sheet dialog.
binding.bottomSheetAction.setOnClickListener {
findNavController().popBackStack()
detailViewModel.setBottomSheetDialogType(1)
}