b06edcaca0e7
begin task 21 motion pass: animate vault list reflow and filter pill selection - LazyColumn entry rows had no animateItem() modifier, so sort changes, deletes, and new entries snapped straight to their new position; added Modifier.animateItem() per row. PeachFilterPills switched between two entirely different composables (bare Text vs Surface-wrapped Text) on selection change with no transition at all - unified into one always-mounted Text with animateColorAsState on background/content color so switching segments now crossfades instead of hard-snapping. Live-verified both: A-Z sort reorders the list and reassigns the accent pill correctly.
- time
- 2026-07-24 13:03 UTC · 3 months ago
- machine
- koh2
- agent
- claude-sonnet-5
- session
- critical-corrections-c1-c7
- parent
- 2f7b3c14b0ec
↓ peach-android-b06edcac.tar.gzatlas at this save
files changed2 files
app/src/main/java/com/peachpasswords/ui/components/PeachSegmentedControl.kt+29 −25app/src/main/java/com/peachpasswords/ui/vault/VaultListScreen.kt+6 −1app/src/main/java/com/peachpasswords/ui/components/PeachSegmentedControl.kt+29 −25
@@ -1,5 +1,8 @@11 package com.peachpasswords.ui.components22 3+import androidx.compose.animation.animateColorAsState4+import androidx.compose.animation.core.tween5+import androidx.compose.foundation.background36 import androidx.compose.foundation.clickable47 import androidx.compose.foundation.layout.Arrangement58 import androidx.compose.foundation.layout.Row@@ -10,9 +13,11 @@1013 import androidx.compose.material3.Surface1114 import androidx.compose.material3.Text1215 import androidx.compose.runtime.Composable16+import androidx.compose.runtime.getValue1317 import androidx.compose.ui.Alignment1418 import androidx.compose.ui.Modifier1519 import androidx.compose.ui.draw.clip20+import androidx.compose.ui.graphics.Color1621 import androidx.compose.ui.unit.dp1722 import com.peachpasswords.ui.theme.LocalPeachAccent1823 import com.peachpasswords.ui.theme.PeachSpacing@@ -91,31 +96,30 @@9196 ) {9297 segments.forEach { segment ->9398 val selected = segment.key == selectedKey94- if (selected) {95- Surface(96- color = accent,97- contentColor = peachColors.Bg,98- shape = RoundedCornerShape(999.dp),99- ) {100- Text(101- segment.label.uppercase(),102- style = PeachType.sortPill,103- modifier = Modifier104- .clip(RoundedCornerShape(999.dp))105- .clickable { onSelected(segment.key) }106- .padding(horizontal = PeachSpacing.sm, vertical = PeachSpacing.xs),107- )108- }109- } else {110- Text(111- segment.label.uppercase(),112- color = peachColors.T2,113- style = PeachType.sortPill,114- modifier = Modifier115- .clickable { onSelected(segment.key) }116- .padding(vertical = PeachSpacing.xs),117- )118- }99+ // One shape, always mounted — animating its own background/content100+ // color between segments reads as a smooth crossfade instead of101+ // the old hard swap between two entirely different composables102+ // (a bare Text vs. a Surface-wrapped one) with no transition.103+ val bg by animateColorAsState(104+ if (selected) accent else Color.Transparent,105+ animationSpec = tween(180),106+ label = "pill-bg",107+ )108+ val fg by animateColorAsState(109+ if (selected) peachColors.Bg else peachColors.T2,110+ animationSpec = tween(180),111+ label = "pill-fg",112+ )113+ Text(114+ segment.label.uppercase(),115+ color = fg,116+ style = PeachType.sortPill,117+ modifier = Modifier118+ .clip(RoundedCornerShape(999.dp))119+ .background(bg)120+ .clickable { onSelected(segment.key) }121+ .padding(horizontal = PeachSpacing.sm, vertical = PeachSpacing.xs),122+ )119123 }120124 }121125 }app/src/main/java/com/peachpasswords/ui/vault/VaultListScreen.kt+6 −1
@@ -235,6 +235,10 @@235235 else menuEntry = entry236236 },237237 onSwipeCopy = { vm.copyPassword(entry) },238+ // Sort changes, deletes, and new entries used to snap239+ // rows straight to their new position — animate the240+ // reflow instead.241+ modifier = Modifier.animateItem(),238242 )239243 }240244 item {@@ -600,6 +604,7 @@600604 onTap: () -> Unit,601605 onLongPress: () -> Unit,602606 onSwipeCopy: () -> Unit,607+ modifier: Modifier = Modifier,603608 ) {604609 val context = LocalContext.current605610 val accent = LocalPeachAccent.current@@ -616,7 +621,7 @@616621 val meta = remember(entry) { entry.domainKeyOrUser() }617622 618623 Row(619- modifier = Modifier624+ modifier = modifier620625 .fillMaxWidth()621626 .clip(RoundedCornerShape(7.dp))622627 .background(rowBg)