feat: Enhance screen transition animations and preserve state during navigation for improved user experience

This commit is contained in:
k1ngsterr1
2026-01-13 18:09:15 +05:00
parent 4689dce862
commit 435c07bf01
2 changed files with 122 additions and 16 deletions

View File

@@ -6,6 +6,8 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.*
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
@@ -19,6 +21,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.rosetta.messenger.data.AccountManager
@@ -231,12 +234,82 @@ fun MainScreen(
var selectedUser by remember { mutableStateOf<SearchUser?>(null) }
var showSearchScreen by remember { mutableStateOf(false) }
// Анимированный переход между экранами
// Анимированный переход между экранами - Telegram-style
AnimatedContent(
targetState = Triple(selectedUser, showSearchScreen, Unit),
transitionSpec = {
// Только плавный fade без смещения - чтобы header не прыгал
fadeIn(
val isEnteringChat = targetState.first != null && initialState.first == null
val isExitingChat = targetState.first == null && initialState.first != null
val isEnteringSearch = targetState.second && !initialState.second
val isExitingSearch = !targetState.second && initialState.second
when {
// 🚀 Вход в чат - slide справа + fade (как Telegram)
isEnteringChat -> {
slideInHorizontally(
initialOffsetX = { fullWidth -> fullWidth / 3 }, // Начинаем на 1/3 экрана справа
animationSpec = tween(
durationMillis = 250,
easing = FastOutSlowInEasing
)
) + fadeIn(
initialAlpha = 0.3f,
animationSpec = tween(
durationMillis = 200,
easing = FastOutSlowInEasing
)
) togetherWith slideOutHorizontally(
targetOffsetX = { fullWidth -> -fullWidth / 6 }, // Список уходит немного влево
animationSpec = tween(
durationMillis = 250,
easing = FastOutSlowInEasing
)
) + fadeOut(
targetAlpha = 0.5f,
animationSpec = tween(
durationMillis = 200,
easing = FastOutSlowInEasing
)
)
}
// 🔙 Выход из чата - slide вправо + fade (быстрее)
isExitingChat -> {
slideInHorizontally(
initialOffsetX = { fullWidth -> -fullWidth / 6 }, // Список возвращается слева
animationSpec = tween(
durationMillis = 200,
easing = FastOutSlowInEasing
)
) + fadeIn(
initialAlpha = 0.5f,
animationSpec = tween(
durationMillis = 180,
easing = FastOutSlowInEasing
)
) togetherWith slideOutHorizontally(
targetOffsetX = { fullWidth -> fullWidth / 2 }, // Чат уходит вправо
animationSpec = tween(
durationMillis = 200,
easing = FastOutSlowInEasing
)
) + fadeOut(
animationSpec = tween(
durationMillis = 180,
easing = FastOutSlowInEasing
)
)
}
// 🔍 Вход в поиск - slide снизу
isEnteringSearch -> {
slideInVertically(
initialOffsetY = { fullHeight -> fullHeight / 4 },
animationSpec = tween(
durationMillis = 220,
easing = FastOutSlowInEasing
)
) + fadeIn(
animationSpec = tween(
durationMillis = 200,
easing = FastOutSlowInEasing
@@ -246,7 +319,39 @@ fun MainScreen(
durationMillis = 150,
easing = FastOutSlowInEasing
)
) using SizeTransform(clip = false)
)
}
// ❌ Выход из поиска - slide вниз
isExitingSearch -> {
fadeIn(
animationSpec = tween(
durationMillis = 180,
easing = FastOutSlowInEasing
)
) togetherWith slideOutVertically(
targetOffsetY = { fullHeight -> fullHeight / 4 },
animationSpec = tween(
durationMillis = 200,
easing = FastOutSlowInEasing
)
) + fadeOut(
animationSpec = tween(
durationMillis = 150,
easing = FastOutSlowInEasing
)
)
}
// Default fade
else -> {
fadeIn(
animationSpec = tween(durationMillis = 200)
) togetherWith fadeOut(
animationSpec = tween(durationMillis = 150)
)
}
}
},
label = "screenNavigation"
) { (user, isSearchOpen, _) ->

View File

@@ -14,6 +14,7 @@ import androidx.compose.material.icons.outlined.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -194,9 +195,9 @@ fun ChatsListScreen(
// Status dialog state
var showStatusDialog by remember { mutableStateOf(false) }
var visible by remember { mutableStateOf(false) }
LaunchedEffect(Unit) { visible = true }
// 🔥 Используем rememberSaveable чтобы сохранить состояние при навигации
// Header сразу visible = true, без анимации при возврате из чата
var visible by rememberSaveable { mutableStateOf(true) }
// Dev console dialog - commented out for now
/*