I've found a bug when using onBackPressedDispatcher in a Jetpack compose app.
Here's a simple example to demonstrate the bug. Imagine that the app has the following setup:
in MainActivity onCreate
method I have the following line:
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
For custom handling of application wide backstack
With this setup navigation back and forth works correctly. However, if you bring the activity to onPause state (e.g. by clicking on the device home button and opening the app again) Jetpack Compose navigation somehow breaks and it defaults to the activity navigation implemented with onBackPressedDispatcher instead of composable screens navigation.
In the following video you can see:
I've published this demo project on GitHub, so you can check it out yourself: https://github.com/konnovdev/NavigationBugDemo/tree/main
I've tried moving onBackPressedDispatcher subscription to different lifecycle callbacks inside of activity but it didn't make any difference. Note that in this particular app example onBackPressedDispatcher looks redundant as there is only one composable nav graph that has its own navigation handling, but in my real app I do rely on this mechanism.
Am I missing something here? I'm confused why is onPause state breaks the navigation. And I'm not able to find a good workaround for this issue
update, best fix, remove "this" from OnBackpressCallback
onBackPressedDispatcher.addCallback(onBackPressedCallback)
fix (old)
connected fragment navcontroller and mainactivity navcontroller to handleOnBackPressed properly, through a sharedviewmodel.
private val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (!navController.navigateUp()) {
println("No more back navigation, can exit app or do any other task")
finish() // Close the app or replace with custom back press handling
}
}
and in oncreate
sharedViewModel.navController.observe(this) { controller ->
navController = controller
// Setup back press callback with NavController
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
}
viewmodel
class SharedViewModel : ViewModel() {
private val _navController = MutableLiveData<NavController>()
val navController: LiveData<NavController> = _navController
fun setNavController(controller: NavController) {
_navController.value = controller
}
}
and in fragment, setNavController
NavigationBugDemoTheme {
val navController = rememberNavController()
sharedViewModel.setNavController(navController)
//rest of the code