voyager icon indicating copy to clipboard operation
voyager copied to clipboard

How to access `navigator` in root Navigator's `onBackPressed` handler?

Open walfud opened this issue 1 year ago • 3 comments

I have a very easy case, just accessing navigator instance in onBackPressed handler. Because LocalNavigator.currentOrThrow need in Composable context, I can't use it in that handler. how to do that?

Example:

@Composable
fun App() {
    MaterialTheme {
        Navigator(
            screen = SplashScreen(),
            onBackPressed = { screen ->
               // <<------- I want accessing `navigator` instance here!!!
                return@Navigator true
            }
        ) { navigator ->
        }
    }
}

Thanks~

walfud avatar Feb 12 '24 03:02 walfud

I think that it should expose navigator instance as parameter in onBackPressed handler, right? @DevSrSouza

walfud avatar Feb 12 '24 03:02 walfud

You can't. This is a common use case, the onBackPressed API currently does not allow this use case.

There is this open PR that fixs some bugs related to nested navigators https://github.com/adrielcafe/voyager/pull/273. I plan to get on back on it soon.

Solution I propose for now, that is what I'm currently using it in personal projects is to replicated what Voyager navigators does under the hood.

This snippet was taken from the Voyager implementation here https://github.com/adrielcafe/voyager/blob/a28fd4dab8fbdd88943974b6df4c8c77f587defc/voyager-navigator/src/commonMain/kotlin/cafe/adriel/voyager/navigator/internal/NavigatorBackHandler.kt#L11

Navigator(...) { navigator ->
   BackHandler(
            enabled = navigator.canPop || navigator.parent?.canPop ?: false,
            onBack = {
               // DO anything here with the `navigator`
                if (navigator.pop().not()) {
                        navigator.parent?.pop()
                    }
            }
    )

   CurrentScreen()
}

DevSrSouza avatar Feb 22 '24 17:02 DevSrSouza

You don't need to access navigator instance in Navigator onBackPressed. Here is how I used

 Navigator(LoadingScreen, onBackPressed = { currentScreen ->
                    when (currentScreen.key) {
                        LoadingScreen.key, IntroductionScreen.key, WelcomeScreen.key -> return@Navigator false
                        else -> return@Navigator true
                    }
                }) { navigator -> ...}

In your Screen, use (you can access navigator in screen easily)

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()
        }
    }
}

akardas16 avatar Apr 06 '24 18:04 akardas16