Home > Software design >  jetpack Compose implement "Home button" functionality on backpress
jetpack Compose implement "Home button" functionality on backpress

Time:04-29

I have navigation via NavController. For some destinations i clear backstack like

navController.navigate(nextScreen) { 
    popUpTo(key) { inclusive = true } 
}

and when system "back" button is pressed its returns on home screen. The tray is still holds the app, but when i try continue using the app, main activity destroys and recreates. How can i implement functionality like "system home button" when "system back button" is pressed and backstack is empty? (it means not to destroy activity, but only implicitly call onPause() like it works with "system home button")

UPDATE

I have a registration flow with 10 screens. On each screen i clear backstack, because of buisness logic. For example, user passed 4/10 of registration flow screens. If he press home button, the app would go to background, and when user return to the app it opens with the same state of 5th screen. But if the user press back button, when he returns to the app the navigations would be from startDestination i.e 1 of the 10 screen. I need pass the app to the background like home button do.

CodePudding user response:

Solving your problem is super-easy, barely an inconvenience. You see, the problem is not that the activity is being cleared on back-press. The real highlight here are the mechanics of Compose. Not gonna stretch it out, simply put - when you press the back button and your activity is "paused", the Composables get destroyed because they are no longer on screen. That is just how Compose is built (for performance) and it makes much sense too. Even if you navigate to another screen inside your app while still inputting your login credentials, and then go back to the login screen, the state would have been cleared, since all those Composables are destroyed the moment they go off-screen.

Now, that's where ViewModels are supposed to help. The state of the TextFields that you might be saving within the Composable's body actually needs to be saved inside a ViewModel, which persists even configuration changes, i.e., the activity is recreated but the ViewModel persists until your app is killed. So, just create a value in the ViewModel, like

var userName by mutableStateOf("")

and then pass that as the value of the TextField, then update it in the onValueChange like you would do with the value defined in the Composable.

Easy,

EDIT

Actually super-easy, barely an inconvenience -- You see Compose provides a BackHandler specifically meant to intercept system back presses. Over there, you could simply call an intent, that triggers an action equivalent to the system Home Press. The intent is as follows:

Intent(Intent.ACTION_MAIN).apply{
 addCategory(Intent.CATEGORY_HOME)
}.let { startActivity(it) }

Easy,

CodePudding user response:

Thanks @MARSK for answer. In the end i have somethink like this

    val context = LocalContext.current
    val callback = object: OnBackPressedCallback(
        true
    ) {
        override fun handleOnBackPressed() {
            if (navController.previousBackStackEntry == null) {
                Intent(Intent.ACTION_MAIN).apply{
                    addCategory(Intent.CATEGORY_HOME)
                }.let { context.startActivity(it) }
            } else {
                navController.popBackStack()
            }
        }
    }
    context.getActivity()?.let { activity ->
        val dispatcher = activity.onBackPressedDispatcher
        dispatcher.addCallback(activity, callback)
        navController.setLifecycleOwner(activity)
        navController.setOnBackPressedDispatcher(dispatcher)
    }
  • Related