feat(nav+fav): порядок меню, анимация иконки при выборе, индикатор на избранном

- Порядок нижнего меню: Радио · Избранное · История · Чарты · Запись · Настройки.
- Иконка вкладки при выборе делает упругий scale-«поп» (spring MediumBouncy) —
  в нижнем баре и боковом рейле.
- На экране «Избранное» играющая станция теперь подсвечивается так же, как на
  главной: вращающееся свечение под обложкой + индикатор-эквалайзер в углу
  (FavoritesViewModel отдаёт playingStationId/isPlaying из PlayerController,
  FavoritesScreen передаёт isCurrent/isPlaying в StationCard).
This commit is contained in:
nk
2026-06-07 17:06:28 +03:00
parent d63c1d4187
commit a5d9a06c3f
3 changed files with 34 additions and 4 deletions

View File

@@ -2,6 +2,9 @@ package com.radiola.ui.navigation
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandHorizontally
import androidx.compose.animation.fadeIn
@@ -28,11 +31,13 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.compose.currentBackStackEntryAsState
@@ -43,9 +48,9 @@ import com.radiola.ui.theme.RadiolaTheme
// при холодном старте может содержать null (порядок инициализации Kotlin).
private val navItems = listOf(
NavDestinations.Stations,
NavDestinations.Charts,
NavDestinations.Favorites,
NavDestinations.History,
NavDestinations.Charts,
NavDestinations.Recordings,
NavDestinations.Settings
)
@@ -144,6 +149,13 @@ private fun VerticalPillTab(
animationSpec = tween(Motion.Medium),
label = "railTabFg"
)
val pop = remember { Animatable(1f) }
LaunchedEffect(selected) {
if (selected) {
pop.snapTo(0.5f)
pop.animateTo(1f, spring(dampingRatio = Spring.DampingRatioMediumBouncy, stiffness = 620f))
}
}
Row(
modifier = Modifier
.size(52.dp)
@@ -161,7 +173,7 @@ private fun VerticalPillTab(
imageVector = icon,
contentDescription = label,
tint = content,
modifier = Modifier.size(22.dp)
modifier = Modifier.size(22.dp).scale(pop.value)
)
}
}
@@ -185,6 +197,14 @@ private fun PillTab(
animationSpec = tween(Motion.Medium),
label = "tabFg"
)
// Упругий «поп» иконки при выборе вкладки — маленькая приятная деталь.
val pop = remember { Animatable(1f) }
LaunchedEffect(selected) {
if (selected) {
pop.snapTo(0.5f)
pop.animateTo(1f, spring(dampingRatio = Spring.DampingRatioMediumBouncy, stiffness = 620f))
}
}
Row(
modifier = modifier
.fillMaxWidth()
@@ -204,7 +224,7 @@ private fun PillTab(
imageVector = icon,
contentDescription = label,
tint = content,
modifier = Modifier.height(22.dp).width(22.dp)
modifier = Modifier.height(22.dp).width(22.dp).scale(pop.value)
)
}
}