Skip to content

Commit

Permalink
Merge pull request #23 from Javernaut/improvement/navigation
Browse files Browse the repository at this point in the history
Using Shared Axis transitions for navigation
  • Loading branch information
Javernaut authored Apr 27, 2024
2 parents 0c6a1b4 + b2f9d4c commit ac8ec5a
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.javernaut.whatthecodec.compose.navigation

import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost

@Composable
fun MaterialNavHost(
navController: NavHostController,
startDestination: String,
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.Center,
route: String? = null,
builder: NavGraphBuilder.() -> Unit
) {
val defaultSlideDistance = rememberSlideDistance()
NavHost(
navController = navController,
startDestination = startDestination,
modifier = modifier,
contentAlignment = contentAlignment,
route = route,
enterTransition = {
materialSharedAxisXIn(true, defaultSlideDistance)
},
exitTransition = {
materialSharedAxisXOut(true, defaultSlideDistance)
},
popEnterTransition = {
materialSharedAxisXIn(false, defaultSlideDistance)
},
popExitTransition = {
materialSharedAxisXOut(false, defaultSlideDistance)
},
builder = builder
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.javernaut.whatthecodec.compose.navigation

import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.FastOutLinearInEasing
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

// Original implementation was taken from here
// https://github.com/fornewid/material-motion-compose/blob/main/core/src/main/java/soup/compose/material/motion/animation/MaterialSharedAxis.kt

private object MotionConstants {
const val DefaultMotionDuration: Int = 300
val DefaultSlideDistance: Dp = 30.dp
}


/**
* Returns the provided [Dp] as an [Int] value by the [LocalDensity].
*
* @param slideDistance Value to the slide distance dimension, 30dp by default.
*/
@Composable
fun rememberSlideDistance(
slideDistance: Dp = MotionConstants.DefaultSlideDistance,
): Int {
val density = LocalDensity.current
return remember(density, slideDistance) {
with(density) { slideDistance.roundToPx() }
}
}

private const val ProgressThreshold = 0.35f

private val Int.ForOutgoing: Int
get() = (this * ProgressThreshold).toInt()

private val Int.ForIncoming: Int
get() = this - this.ForOutgoing

/**
* [materialSharedAxisXIn] allows to switch a layout with shared X-axis enter transition.
*
* @param forward whether the direction of the animation is forward.
* @param slideDistance the slide distance of the enter transition.
* @param durationMillis the duration of the enter transition.
*/
fun materialSharedAxisXIn(
forward: Boolean,
slideDistance: Int,
durationMillis: Int = MotionConstants.DefaultMotionDuration,
): EnterTransition = slideInHorizontally(
animationSpec = tween(
durationMillis = durationMillis,
easing = FastOutSlowInEasing
),
initialOffsetX = {
if (forward) slideDistance else -slideDistance
}
) + fadeIn(
animationSpec = tween(
durationMillis = durationMillis.ForIncoming,
delayMillis = durationMillis.ForOutgoing,
easing = LinearOutSlowInEasing
)
)

/**
* [materialSharedAxisXOut] allows to switch a layout with shared X-axis exit transition.
*
* @param forward whether the direction of the animation is forward.
* @param slideDistance the slide distance of the exit transition.
* @param durationMillis the duration of the exit transition.
*/
fun materialSharedAxisXOut(
forward: Boolean,
slideDistance: Int,
durationMillis: Int = MotionConstants.DefaultMotionDuration,
): ExitTransition = slideOutHorizontally(
animationSpec = tween(
durationMillis = durationMillis,
easing = FastOutSlowInEasing
),
targetOffsetX = {
if (forward) -slideDistance else slideDistance
}
) + fadeOut(
animationSpec = tween(
durationMillis = durationMillis.ForOutgoing,
delayMillis = 0,
easing = FastOutLinearInEasing
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import androidx.compose.foundation.background
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import com.javernaut.whatthecodec.compose.navigation.MaterialNavHost
import com.javernaut.whatthecodec.home.ui.navigation.HomeRoute
import com.javernaut.whatthecodec.home.ui.navigation.homeScreen
import com.javernaut.whatthecodec.settings.navigation.navigateToSettings
Expand All @@ -14,10 +14,10 @@ import com.javernaut.whatthecodec.settings.navigation.settingsScreen
@Composable
fun WhatTheCodecApp() {
val navController = rememberNavController()
NavHost(
MaterialNavHost(
navController = navController,
startDestination = HomeRoute,
modifier = Modifier.background(MaterialTheme.colorScheme.background)
modifier = Modifier.background(MaterialTheme.colorScheme.background),
) {
homeScreen(
onSettingsClicked = navController::navigateToSettings
Expand Down

0 comments on commit ac8ec5a

Please sign in to comment.