adrielcafe / voyager

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

Transitions in between Tab Navigator #265

Open ShivamKumarJha opened 11 months ago

ShivamKumarJha commented 11 months ago

Hi, Is there any way to have transitions such as slide or fade in between tab navigation? Thanks

ShivamKumarJha commented 11 months ago

I'm able to achieve transitions in between tabs with some source mode modifications. However this is not restoring the previous saved state.

class TabNavigator internal constructor(
    internal val navigator: Navigator
) {

    var current: Tab
        get() = navigator.lastItem as Tab
        set(tab) {
            lastEvent =
                if (tab.options.index >= current.options.index) StackEvent.Push else StackEvent.Pop
            navigator.replaceAll(tab)
        }

    var lastEvent: StackEvent = StackEvent.Push

    @Composable
    fun saveableState(
        key: String,
        tab: Tab = current,
        content: @Composable () -> Unit
    ) {
        navigator.saveableState(key, tab, content = content)
    }
}

@Composable
fun SlideTransition(
    navigator: TabNavigator,
    modifier: Modifier = Modifier,
    orientation: SlideOrientation = SlideOrientation.Horizontal,
    animationSpec: FiniteAnimationSpec<IntOffset> = spring(
        stiffness = Spring.StiffnessMediumLow,
        visibilityThreshold = IntOffset.VisibilityThreshold
    ),
    content: TabNavigatorContent = { it.current.Content() }
) {
    ScreenTransition(
        navigator = navigator,
        modifier = modifier,
        content = content,
        transition = {
            val (initialOffset, targetOffset) = when (navigator.lastEvent) {
                StackEvent.Pop -> ({ size: Int -> -size }) to ({ size: Int -> size })
                else -> ({ size: Int -> size }) to ({ size: Int -> -size })
            }

            when (orientation) {
                SlideOrientation.Horizontal ->
                    slideInHorizontally(animationSpec, initialOffset) togetherWith
                            slideOutHorizontally(animationSpec, targetOffset)

                SlideOrientation.Vertical ->
                    slideInVertically(animationSpec, initialOffset) togetherWith
                            slideOutVertically(animationSpec, targetOffset)
            }
        }
    )
}

@Composable
fun ScreenTransition(
    navigator: TabNavigator,
    transition: AnimatedContentTransitionScope<Tab>.() -> ContentTransform,
    modifier: Modifier = Modifier,
    content: TabNavigatorContent = { it.current.Content() }
) {
    AnimatedContent(
        targetState = navigator.current,
        transitionSpec = transition,
        modifier = modifier
    ) { tab ->
        navigator.saveableState("transition", tab) {
            content(navigator)
        }
    }
}
cedrickcooke commented 8 months ago

I was able to get state-restoration working with a small modification:

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