Handling Back Presses in Jetpack Compose and OnBackInvokedCallback
In our previous articles, we discussed the deprecation of the onBackPressed method and the introduction of the onBackPressedDispatcher in Android’s Jetpack Compose library. As we continue to explore the new features and changes in Jetpack Compose, it’s important to understand how to handle back presses from directly composables. In this article, we will dive into the details of how to handle back presses from directly composables and provide a workaround for the new OnBackInvokedCallback. Whether you’re a seasoned Android developer or just getting started with Jetpack Compose, this article will provide valuable insights and techniques for managing back presses in your composable UI.
First of all, we shall begin with how to handle back presses in composables. As you may know that we have OnBackPressedCallback
which is used for to get the callbacks of back presses. The callback
First of all, we shall begin with how to handle back presses in composables. As you may know that we have OnBackPressedCallback which is used for to get the callbacks of back presses. The callback takes a boolean parameter which tells whether the back press is handled or not and a lambda which is called when the back press is handled. Here is a simple example of how to create it.
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
Log.d("TAG", "OnBackPressed")
}
}
In compose, we have a similar function called BackHandler. It, basically, is a composable which takes a lambda which is called when the back press is handled. The BackHandler composable uses the OnBackPressedCallback internally. It adds and removes the callback when the composable is added and removed from the composition respectively. Here, you can see how to use it.
BackHandler(true) {
Log.d("TAG", "OnBackPressed")
}
Note that due the fact that the function uses the OnBackPressedCallback internally, it would block and consume the upcoming back presses. So, if the lifecycle owner uses its own OnBackPressedCallback, then it would not be called.
Now, let us see how to handle OnBackInvokedCallback in compose.
Recall that the OnBackInvokedCallback
used for the predictive back gesture. It tells the OS that the app has handled the back gesture and the OS should not show the back gesture hint. Before we go further, let us enable the callback in the app. To do that, we need to add the following line to the manifest file.
android:enableOnBackInvokedCallback="true"
Please do not forget to enable predictive back gestures in a Android 13 or higher device. Otherwise, the callback would not be called.
Unfortunately, there is no direct way to use the callback in compose. But, I wrote a simple composable which uses the OnBackInvokedCallback
internally and calls the OnBackInvokedCallback when the back press is handled. Just like the BackHandler. Here is the code.
@Composable
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun BackInvokeHandler(
handleBackHandler: Boolean,
priority : Int = OnBackInvokedDispatcher.PRIORITY_DEFAULT,
callback : () -> Unit = {}
) {
val backInvokedCallback = remember {
OnBackInvokedCallback {
callback()
}
}
val activity = when(LocalLifecycleOwner.current) {
is MainActivity -> LocalLifecycleOwner.current as MainActivity
is Fragment -> (LocalLifecycleOwner.current as Fragment).requireActivity() as MainActivity
else -> {
val context = LocalContext.current
if (context is MainActivity) {
context
} else {
throw IllegalStateException("LocalLifecycleOwner is not MainActivity or Fragment")
}
}
}
if (handleBackHandler) {
activity.onBackInvokedDispatcher.registerOnBackInvokedCallback(priority, backInvokedCallback)
}
LaunchedEffect(handleBackHandler) {
if (!handleBackHandler) {
activity.onBackInvokedDispatcher.unregisterOnBackInvokedCallback(backInvokedCallback)
}
}
DisposableEffect(activity.lifecycle, activity.onBackInvokedDispatcher) {
onDispose {
activity.onBackInvokedDispatcher.unregisterOnBackInvokedCallback(backInvokedCallback)
}
}
}
As you may notice that the composable requires Android 13 or higher. That is simple because the OnBackInvokedCallback is only available in Android 13 or higher. And, I must add that the callback would block the upcoming back swipes. So, if the lifecycle owner uses its own OnBackInvokedCallback
, then it would not be called. Even more, if you use the BackHandler composable and the BackInvokedHandler, the Android OS would use the BackInvokedHandler composable and ignore the BackHandler composable. Or in other words, when they both are enabled the Android OS would prefer the BackInvokedHandler composable.
All in all, I hope that this article was helpful. If you have any questions or suggestions, please let me know in the comments. I also created a GitHub repository about this topic with a little box selecting app. You can find it below. Thank you for reading. See you in the next article.
Do not forget to follow me for upcoming blogs/articles.
Love you all.
Stay tune for upcoming blogs.
Take care.