Refactor SunCard entrance animation to be shared

Moved the entrance animation logic from SunCard to MainScreen so that all cards share a single entrance animation instance. SunCard now receives entranceProgress as a parameter, simplifying its internal state and ensuring consistent animation timing across cards.
This commit is contained in:
Paweł Orzech 2026-01-28 00:17:30 +01:00
parent 87ad810392
commit 34c842c2d5
No known key found for this signature in database
2 changed files with 17 additions and 10 deletions

View file

@ -1,5 +1,8 @@
package com.sunzones.ui.main package com.sunzones.ui.main
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@ -15,6 +18,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -47,6 +51,15 @@ fun MainScreen(
val uiState by viewModel.uiState.collectAsState() val uiState by viewModel.uiState.collectAsState()
var showAddSheet by remember { mutableStateOf(false) } var showAddSheet by remember { mutableStateOf(false) }
// Entrance animation — runs once on screen load, shared by all cards
val entranceProgress = remember { Animatable(0f) }
LaunchedEffect(Unit) {
entranceProgress.animateTo(
targetValue = 1f,
animationSpec = tween(durationMillis = 1200, easing = FastOutSlowInEasing)
)
}
LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
viewModel.refresh() viewModel.refresh()
} }
@ -110,7 +123,8 @@ fun MainScreen(
onDelete = { viewModel.deleteLocation(location.id) }, onDelete = { viewModel.deleteLocation(location.id) },
monthlyDaylight = uiState.yearlyDaylight[location.id], monthlyDaylight = uiState.yearlyDaylight[location.id],
gradientColors = blendedGradient, gradientColors = blendedGradient,
pageOffset = pageOffset pageOffset = pageOffset,
entranceProgress = entranceProgress.value
) )
} }

View file

@ -86,6 +86,7 @@ fun SunCard(
monthlyDaylight: List<MonthDaylight>?, monthlyDaylight: List<MonthDaylight>?,
gradientColors: List<Color> = listOf(NightTop, NightBottom), gradientColors: List<Color> = listOf(NightTop, NightBottom),
pageOffset: Float = 0f, pageOffset: Float = 0f,
entranceProgress: Float = 1f,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
var showDeleteDialog by remember { mutableStateOf(false) } var showDeleteDialog by remember { mutableStateOf(false) }
@ -99,15 +100,7 @@ fun SunCard(
} }
} }
// Step 5: Entrance animation val entrance = entranceProgress
val entranceProgress = remember { Animatable(0f) }
LaunchedEffect(Unit) {
entranceProgress.animateTo(
targetValue = 1f,
animationSpec = tween(durationMillis = 1200, easing = FastOutSlowInEasing)
)
}
val entrance = entranceProgress.value
// Step 4: Living sky ambient // Step 4: Living sky ambient
val ambientTransition = rememberInfiniteTransition(label = "ambient") val ambientTransition = rememberInfiniteTransition(label = "ambient")