Reza Zeraati
Reza Zeraati

Reputation: 423

How to set Double back press Exit in Jetpack Compose?

There are some tutorials on YouTube about how to set double back press exit in XML android, but most of them are in JAVA and None of them are in Jetpack Compose.

So how can we set that Double back press in Jetpack Compose?

I mean that thing that ask us in a Toast to press back again if we are sure to Exit. Thanks for help

Upvotes: 9

Views: 2965

Answers (4)

h8leet
h8leet

Reputation: 41

The smaller implementation with coroutine scope and one state for enable/disable BackHandler.

The BackHandler is making Toast, do self-disabling and launch delayed enabling in coroutine scope.

@Composable
fun DoubleBackPressHandler(enabled: Boolean = true) {
    val context = LocalContext.current
    val scope = rememberCoroutineScope()
    val isBackPressed = remember { mutableStateOf(false) }
    BackHandler(enabled && !isBackPressed.value) {
        isBackPressed.value = true
        Toast.makeText(context, "Press back again to exit", Toast.LENGTH_SHORT).show()
        scope.launch {
            delay(2000L)
            isBackPressed.value = false
        }
    }
}

Upvotes: 1

yong wei
yong wei

Reputation: 183

    @Composable
    fun TwiceBackHandler(
        enabled: Boolean = true,
        duration: Duration = 2.seconds,
        onFirstBack: () -> Unit,
        onBack: () -> Unit
    ) {
        val currentOnBack by rememberUpdatedState(onBack)
        val currentOnFirstBack by rememberUpdatedState(onFirstBack)
        var exit by remember { mutableStateOf(false) }
        LaunchedEffect(key1 = exit) {
            if (exit) {
                delay(duration.inWholeMilliseconds)
                exit = false
            }
        }
        val backCallback = remember {
            object : OnBackPressedCallback(enabled) {
                override fun handleOnBackPressed() {
                    if (exit) {
                        currentOnBack()
                    } else {
                        exit = true
                        currentOnFirstBack()
                    }
                }
            }
        }
        SideEffect {
            backCallback.isEnabled = enabled
        }
        val backDispatcher = checkNotNull(LocalOnBackPressedDispatcherOwner.current) {
            "No OnBackPressedDispatcherOwner was provided via LocalOnBackPressedDispatcherOwner"
        }.onBackPressedDispatcher
        val lifecycleOwner = LocalLifecycleOwner.current
        DisposableEffect(lifecycleOwner, backDispatcher) {
            backDispatcher.addCallback(lifecycleOwner, backCallback)
            onDispose {
                backCallback.remove()
            }
        }
    }

You can use it

@Composable
fun BackDemo() {
    Box {
        Text(text = "demo")
        TwiceBackHandler(
            onFirstBack = {
            Toast.makeText(context, "Press again to exit", Toast.LENGTH_SHORT).show()
            }) {
         
            finishActivity()
        }
    }
}

Upvotes: 0

OCN Yang
OCN Yang

Reputation: 31

@Thrancian 's answer didn't seem to go the way I wanted it to. Because in my tests, I had to hit the back button three times to exit the app.

But I modified it to meet my needs.

@Composable
fun BackPressSample2() {
    var exit by remember { mutableStateOf(false) }
    val context = LocalContext.current

    LaunchedEffect(key1 = exit) {
        if (exit) {
            delay(2000)
            exit = false
        }
    }

    BackHandler(enabled = true) {
        if (exit) {
            context.startActivity(Intent(Intent.ACTION_MAIN).apply {
                addCategory(Intent.CATEGORY_HOME)
                flags = Intent.FLAG_ACTIVITY_NEW_TASK
            })
        } else {
            exit = true
            Toast.makeText(context, "Press again to exit", Toast.LENGTH_SHORT).show()
        }
    }
}

Upvotes: 2

Thracian
Thracian

Reputation: 67199

This sample shows Toast on first touch and waits for 2 seconds to touch again to exit app otherwise goes back to Idle state.

sealed class BackPress {
    object Idle : BackPress()
    object InitialTouch : BackPress()
}

@Composable
private fun BackPressSample() {
    var showToast by remember { mutableStateOf(false) }

    var backPressState by remember { mutableStateOf<BackPress>(BackPress.Idle) }
    val context = LocalContext.current

    if(showToast){
        Toast.makeText(context, "Press again to exit", Toast.LENGTH_SHORT).show()
        showToast= false
    }


    LaunchedEffect(key1 = backPressState) {
        if (backPressState == BackPress.InitialTouch) {
            delay(2000)
            backPressState = BackPress.Idle
        }
    }

    BackHandler(backPressState == BackPress.Idle) {
        backPressState = BackPress.InitialTouch
        showToast = true
    }
}

enter image description here

Upvotes: 13

Related Questions