Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-18 15:52:37 +03:00
parent d42cb2a79e
commit 00c5775f03
7 changed files with 70 additions and 29 deletions

View file

@ -28,6 +28,7 @@ import com.arkivanov.decompose.value.Value
import com.arkivanov.essenty.backhandler.BackHandler
import com.tangem.common.routing.AppRoute
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.haze.ProvideHaze
import com.tangem.core.ui.components.snackbar.TangemSnackbarHost
import com.tangem.core.ui.components.snackbar.TangemTopSnackbarHost
import com.tangem.core.ui.message.EventMessageEffect
@ -72,7 +73,9 @@ internal fun RootContent(
when (val instance = child.instance) {
is RoutingComponent.Child.Initial -> Unit
is RoutingComponent.Child.ComposableComponent -> {
instance.component.Content(Modifier.fillMaxSize())
ProvideHaze {
instance.component.Content(Modifier.fillMaxSize())
}
}
is RoutingComponent.Child.LegacyIntent -> {
// TODO: Remove and use it's own router: [REDACTED_JIRA]

View file

@ -4,10 +4,12 @@ import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.tween
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.layout
import com.arkivanov.decompose.extensions.compose.stack.animation.*
import com.tangem.common.routing.AppRoute
import kotlin.math.abs
object RoutingTransitionAnimationFactory {
@ -18,13 +20,15 @@ object RoutingTransitionAnimationFactory {
is AppRoute.Home,
-> fade(tween(400)).plus(scale(tween(400)))
is AppRoute.Wallet,
-> slideAndFade(directions = setOf(Direction.ENTER_BACK, Direction.EXIT_BACK))
.plus(
scaleWithDirection(
directions = setOf(Direction.ENTER_FRONT, Direction.EXIT_FRONT),
animationSpec = tween(400),
),
)
-> slideAndFade(
slideDirections = setOf(Direction.ENTER_BACK, Direction.EXIT_BACK),
fadeDirections = emptySet(),
).plus(
scaleWithDirection(
directions = setOf(Direction.ENTER_FRONT, Direction.EXIT_FRONT),
animationSpec = tween(400),
),
)
else -> slideAndFade()
}
}
@ -52,28 +56,64 @@ object RoutingTransitionAnimationFactory {
)
}
/**
* @param slideDirections directions in which the horizontal slide is applied.
* `null` (default) means slide in all directions.
* @param fadeDirections directions in which the alpha fade is applied.
* `null` (default) means fade in all directions. Pass `emptySet()` to disable the fade
* entirely useful for screens that own a `hazeEffect` (e.g. WalletTopBar's progressive
* blur), where wrapping the screen in an animated `graphicsLayer { alpha = ... }` causes
* a visible blink over the blurred region.
*/
@Suppress("MagicNumber")
private fun slideAndFade(directions: Set<Direction>? = null): StackAnimator {
private fun slideAndFade(
slideDirections: Set<Direction>? = null,
fadeDirections: Set<Direction>? = null,
): StackAnimator {
val easing = CubicBezierEasing(a = 0.55f, b = 0.0f, c = 0.0f, d = 1f)
return stackAnimator(
val slide = stackAnimator(
animationSpec = tween(durationMillis = 400, easing = easing),
) { factor, direction, content ->
content(
if (directions == null || directions.contains(direction)) {
if (slideDirections == null || slideDirections.contains(direction)) {
Modifier.offsetXFactor(factor)
} else {
Modifier
},
)
}.plus(
fade(
animationSpec = tween(
delayMillis = 50,
durationMillis = 300,
easing = easing,
),
}
val fade = directionalFade(
animationSpec = tween(
delayMillis = 50,
durationMillis = 300,
easing = easing,
),
directions = fadeDirections,
)
return slide.plus(fade)
}
/**
* Like `decompose.fade(...)` but only applies the alpha `graphicsLayer` when `direction`
* is in [directions]. `null` directions = always fade (matches stock `fade()` behavior).
* `emptySet()` directions = never fade (modifier passes through untouched).
*/
private fun directionalFade(
animationSpec: FiniteAnimationSpec<Float>,
directions: Set<Direction>?,
): StackAnimator = stackAnimator(animationSpec) { factor, direction, content ->
content(
if (directions == null || directions.contains(direction)) {
Modifier.graphicsLayer {
alpha = 1f - abs(factor)
compositingStrategy = CompositingStrategy.Offscreen
}
} else {
Modifier
},
)
}