Create / Edit
EditScreen es una sola pantalla con dos usos: crear una carta nueva (tab Create,
isNewCard = true) y editar una carta existente (isEditingCard = true, llamada desde Details).
Props y estado local
Sección titulada «Props y estado local»@Composablefun EditScreen( modifier: Modifier = Modifier, card: RacingCard, title: String = "Edit Card", isNewCard: Boolean = false, onBack: () -> Unit = {}, onSave: (RacingCard) -> Unit = {},)Todo el formulario es estado local inicializado desde card (la carta a editar, o
CardOptions.blank si es nueva):
var name by rememberSaveable { mutableStateOf(card.name) }var cls by rememberSaveable { mutableStateOf(card.cls) }var carNumber by rememberSaveable { mutableStateOf(card.carNumber.ifBlank { (1..99).random().toString() })}var carImageIndex by rememberSaveable { mutableIntStateOf(card.carImageIndex) }var background by rememberSaveable { mutableIntStateOf(card.backgroundIndex) }var badge by rememberSaveable { mutableIntStateOf(card.badgeIndex) }var rolling by remember { mutableStateOf(false) }Notas sobre estos campos:
carNumbernunca se edita a mano: una carta nueva recibe un número 1–99 aleatorio al entrar a la pantalla; una carta existente conserva el suyo.nameestá limitado aMAX_NAME_LENGTH = 15caracteres, forzado en el propioonValueChangedel campo.brandno es un campo del formulario: se deriva del badge elegido víabadgeBrandName(badge)al construir la carta final.
Secciones del formulario
Sección titulada «Secciones del formulario»CenterAlignedTopAppBar(title, back, check ✓)└── (scrollable) ├── CarArtworkPager — preview en vivo, swipe para elegir la foto del auto ├── EditSection "Card" │ ├── Name (OutlinedTextField, contador de caracteres) │ └── Class (DropdownField sobre CardOptions.classes) └── EditSection "Appearance" ├── Card background (SwatchPickerRow sobre CardBackgroundColors) └── Team badge (BadgePickerRow)CarArtworkPager es la pieza más interesante de la sección: un HorizontalPager que envuelve
un CardArtwork completo por página, así que el preview del formulario es visualmente
idéntico a como se verá la carta terminada, incluida la viñeta de color y el badge.
Guardar: dos caminos distintos
Sección titulada «Guardar: dos caminos distintos»FilledIconButton(onClick = { if (isNewCard) { rolling = true } else { onSave(buildCard(card.stats, card.tier)) }})- Editar una carta existente guarda de inmediato, reutilizando
card.statsycard.tier— las estadísticas nunca se vuelven a rifar en una edición. - Crear una carta nueva no llama a
onSavetodavía: activarolling = true, lo que reemplaza todo el contenido de la pantalla porPerformanceRollOverlay.
if (rolling) { val finalStats = remember { PerformanceRoller.rollStats() } val finalTier = remember(finalStats) { PerformanceRoller.tierFor(finalStats) } PerformanceRollOverlay( card = buildCard(finalStats, finalTier), finalStats = finalStats, finalTier = finalTier, onFinished = { onSave(buildCard(finalStats, finalTier)) }, ) return}PerformanceRollOverlay: el reveal estilo casino
Sección titulada «PerformanceRollOverlay: el reveal estilo casino»Ocupa toda la pantalla (fondo NeutralDark al 96% de opacidad) y anima, una estadística a la
vez, un ciclo de “spin” antes de asentarse en el valor final:
suspend fun spin(pool: IntRange, finalValue: Int, format: (Int) -> String, ...) { val steps = 16 for (i in 0 until steps) { setText(format(pool.random())) delay(35L + i * 7L) // cada paso es un poco más lento -> efecto de frenado } setText(format(finalValue)) setDone(true) delay(350)}El orden es siempre velocidad máxima → 0-100 → potencia → peso, en cascada (cada spin se
espera con await antes de arrancar el siguiente). Al terminar las 4:
- Se muestra el chip de rareza (
showTier = true), coloreado contierColor(finalTier). - El auto hace un pequeño “pulso” de escala (1 → 1.12 → 1, con
FastOutSlowInEasing). - Tras una pausa, se llama
onFinished()— que es lo que efectivamente persiste la carta y navega de vuelta a la Gallery.
Timeline del reveal
sequenceDiagram participant Ov as PerformanceRollOverlay Note over Ov: fondo NeutralDark al 96% + auto sin overlays Ov->>Ov: spin(topSpeed) ~16 pasos, delay creciente Ov->>Ov: spin(zeroToHundred) Ov->>Ov: spin(power) Ov->>Ov: spin(weight) Ov->>Ov: showTier = true (chip de rareza) Ov->>Ov: pulso de escala del auto (1 -> 1.12 -> 1) Ov->>Ov: delay(500ms) Ov->>Ov: onFinished()
La pantalla se divide en dos secciones ponderadas (weight(0.55f) para el auto,
weight(0.45f) para las estadísticas) que suman exactamente el alto disponible, así el reveal
completo entra siempre en una sola pantalla sin necesidad de scroll.
