adrielcafe / voyager

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

Make it possible to pass Scaffold PaddingValues to CurrentTab() #409

Open harry248 opened 2 weeks ago

harry248 commented 2 weeks ago

If nesting a Scaffold inside a TabNavigator, the PaddingValues that are passed to the content lambda of the Scaffold can't be passed to CurrentTab(). Would be nice to have some official support.

hristogochev commented 2 weeks ago

You can use the following interface and function to be able to pass the PaddingValues:

@Composable
fun CurrentTabX(
    innerPadding: PaddingValues, snackBarHostState: SnackbarHostState
) {
    val tabNavigator = LocalTabNavigator.current
    val currentTab = tabNavigator.current as TabX

    tabNavigator.saveableState("currentTab") {
        currentTab.Content(innerPadding, snackBarHostState)
    }
}

interface TabX : Tab {

    @Composable
    fun Content(
        innerPadding: PaddingValues,
        snackBarHostState: SnackbarHostState,
    )

    @Composable
    override fun Content() {
        error("Called Content without arguments")
    }
}

Simply inherit from TabX instead of Tab for your Tabs and replace CurrentTab() with CurrentTabX()

hristogochev commented 2 weeks ago

If you also want to then add a fade transition between the Tabs you could use:

typealias TabTransitionContent = @Composable AnimatedVisibilityScope.(Tab) -> Unit

@Composable
fun TabTransition(
    navigator: TabNavigator,
    transition: AnimatedContentTransitionScope<Tab>.() -> ContentTransform,
    modifier: Modifier = Modifier,
    content: TabTransitionContent
) {
    AnimatedContent(
        targetState = navigator.current,
        transitionSpec = transition,
        modifier = modifier
    ) { tab ->
        navigator.saveableState("transition", tab) {
            content(tab)
        }
    }
}

@Composable
fun FadeTabTransition(
    navigator: TabNavigator,
    modifier: Modifier = Modifier,
    animationSpec: FiniteAnimationSpec<Float> = spring(stiffness = Spring.StiffnessMediumLow),
    content: TabTransitionContent
) {
    TabTransition(
        navigator = navigator,
        modifier = modifier,
        content = content,
        transition = { fadeIn(animationSpec = animationSpec) togetherWith fadeOut(animationSpec = animationSpec) }
    )
}

And then call the following for the content of your Scaffold instead of CurrentTabX:

content = { innerPadding ->
    FadeTabTransition(it) { tab ->
        (tab as TabX).Content(innerPadding, snackBarHostState)
    }
},
harry248 commented 5 days ago

Sorry for the late reply @hristogochev... Looks like a nice solution, thank you!