adrielcafe / voyager

🛸 A pragmatic navigation library for Jetpack Compose
https://voyager.adriel.cafe
MIT License
2.27k stars 109 forks source link

Custom back handling depending on the currentScreen #360

Open jortas opened 2 months ago

jortas commented 2 months ago

It is normal that developers might want a different backHandling depending on the current screen. For that I developed a custom solution for my project, it would be nice to have something like this in the base code.

abstract class BaseScreen() : Screen, Parcelable {
    @IgnoredOnParcel
    protected var onBackPressed: (() -> Boolean)? = null

    fun onBackPressed(): Boolean {
        return onBackPressed?.invoke() ?: true
    }
}

And the Screen becomes

@Parcelize
class DifferentScreen : BaseScreen(), Parcelable {

    @Composable
    override fun Content() {
        val navigator = LocalNavigator.currentOrThrow
        val viewModel: DifferentViewModel = getScreenModel()
        LaunchedEffect(viewModel, navigator) {
            onBackPressed = {
                if (viewModel.screenIsLoading()) {
                    false
                } else {
                    true
                }
            }
        }

and on the activity

Navigator(initialScreens,
                 onBackPressed = { (it as? BaseScreen)?.onBackPressed() ?: true },
                 content = { navigator -> FadeTransition(navigator)}
)
ElliotSknr commented 2 months ago

It seems that this sort of pattern already exists, but is unfortunately marked as internal. See here

Also to anyone else trying to use the above code, make sure to have the kotlin-parcelize plugin enabled in your common module.

akardas16 commented 1 month ago

Add below compose to your project and use it like

BackPressHandler {  //back pressed
          //Do Something
  }
@Composable
fun BackPressHandler(
    backPressedDispatcher: OnBackPressedDispatcher? =
        LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher,
    onBackPressed: () -> Unit
) {
    val currentOnBackPressed by rememberUpdatedState(newValue = onBackPressed)

    val backCallback = remember {
        object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                currentOnBackPressed()
            }
        }
    }

    DisposableEffect(key1 = backPressedDispatcher) {
        backPressedDispatcher?.addCallback(backCallback)

        onDispose {
            backCallback.remove()
        }
    }
}