feat(ui): design token discipline, accessibility and i18n pass

- Replace all hardcoded Color literals outside lib/tema with theme tokens (new static brand palette in PluriWaveTokens); media notification uses the brand color instead of the Material default purple
- Favorite button on station cards grows to a 48dp target and becomes an independent semantics node for screen readers (Semantics container fix)
- All flutter_animate call sites route through the PluriAnimate reduced-motion gate (zero direct .animate() left)
- Locale-aware short dates via intl DateFormat (new lib/l10n/formato_fechas.dart) replacing the hardcoded DD/MM/YYYY; proper plural messages for the favorites counter; example stream URL as a localized key - all 13 locales
- Rounded shimmer placeholders matching card radii; shimmer loading state in search instead of a bare spinner; rounded icon variants unified in settings; bottom-sheet conventions on the custom station form
- Fix latent debug crash: vacation editor read AppLocalizations in initState
- 11 new tests (121 total green), flutter analyze clean
This commit is contained in:
2026-06-11 23:42:16 +02:00
parent 52855e75c2
commit 202bef3539
49 changed files with 1108 additions and 175 deletions
@@ -363,30 +363,30 @@ Chain strategy: N/A (local apply)
### S5 pre-work: write failing tests
- [ ] **T-S5-01** [RED] Create `test/widgets/tarjeta_emisora_a11y_test.dart`: favorite `InkWell` has semantic label + `button:true`; size ≥ 48×48 dp (S5-R2-A). **~20 lines.**
- [ ] **T-S5-02** [RED] Add test in `test/tema/pluri_animate_test.dart`: `pluriFadeIn` returns unanimated child when `disableAnimations=true` (S5-R3-A). **~15 lines.**
- [ ] **T-S5-03** [RED] Create `test/pantallas/pantalla_alarmas_fecha_test.dart`: `_fechaCorta` with locale `en-US` returns `DateFormat.yMd('en-US')` result, NOT `11/06/2026` (S5-R4-A). **~15 lines.**
- [ ] **T-S5-04** [RED] Add test `test/pantallas/pantalla_favoritos_plural_test.dart`: plural form changes between 1 and 5 station count strings (S5-R5). **~10 lines.**
- [ ] **T-S5-05** [RED] Add widget test: shimmer present during loading state in `PantallaBuscar` (S5-R6). **~10 lines.**
- [ ] **T-S5-06** [RED] Add unit test: `AudioServiceConfig.notificationColor` equals brand color token (S5-R8). **~10 lines.**
- [x] **T-S5-01** [RED] Create `test/widgets/tarjeta_emisora_a11y_test.dart`: favorite `InkWell` has semantic label + `button:true`; size ≥ 48×48 dp (S5-R2-A). **DONE — RED exposed a REAL merge bug: the card InkWell absorbed the favorite into one semantics node; fixed with `Semantics(container: true)`. Toggled state asserted via `flagsCollection` (Tristate API; `hasFlag` is deprecated).**
- [x] **T-S5-02** [RED] `test/tema/pluri_animate_test.dart`: `pluriFadeIn`/`pluriScaleIn` lock-in (already implemented in S2b) + NEW `pluriFadeSlideIn` (honest RED: method missing). 4 tests. **DONE.**
- [x] **T-S5-03** [RED] `test/pantallas/pantalla_alarmas_fecha_test.dart`: en-US returns `6/11/2026` (NOT `11/06/2026`) + es day/month order. Tests the NEW public helper `fechaCortaLocalizada` (private `_fechaCorta` is untestable; see deviation). **DONE.**
- [x] **T-S5-04** [RED] `test/pantallas/pantalla_favoritos_plural_test.dart`: `stationCount(1)` differs from `stationCount(5)` in en and es. **DONE.**
- [x] **T-S5-05** [RED] `test/pantallas/pantalla_buscar_shimmer_test.dart`: loading shows `TarjetaEmisoraShimmer`, NO `CircularProgressIndicator` (cargando-true EstadoBusqueda subclass seam). **DONE.**
- [x] **T-S5-06** [RED] `test/tema/notification_color_test.dart`: `configuracionAudioService.notificationColor == PluriWaveTokens.brand` (config extracted to a testable top-level const). **DONE.**
### S5 implementation
- [ ] **T-S5-07** [GREEN] Edit all 14+ remaining `Color(0x...)` literal sites identified in explore C3 (files: `lib/pantallas/`, `lib/widgets/`, excluding `pantalla_alarma_sonando.dart` done in S2b): replace with `PluriWaveTokens` or `Theme.of(context).colorScheme` references. **Reqs:** S5-R1. **~30 lines across files.**
- [ ] **T-S5-08** [GREEN] Edit `lib/widgets/tarjeta_emisora.dart` (lines 238-289): wrap mini favorite `InkWell` in `Semantics(button: true, label: l10n.toggleFavorite)`; set `constraints: BoxConstraints(minWidth: 48, minHeight: 48)`. Add `semanticLabel` to `_AssetIcon`/alarm PNG. **Reqs:** S5-R2. **~15 lines.**
- [ ] **T-S5-09** [GREEN] `lib/tema/pluri_animate.dart` already created in S2b (T-S2b-04). Verify tests pass (no new code needed here unless edge case found).
- [ ] **T-S5-10** [GREEN] Edit `lib/pantallas/pantalla_alarmas.dart` `_fechaCorta` (line 1114): replace hardcoded format string with `intl.DateFormat.yMd(Localizations.localeOf(context).toLanguageTag()).format(date)`. **Reqs:** S5-R4. **~5 lines.**
- [ ] **T-S5-11** [GREEN] Edit `lib/pantallas/pantalla_favoritos.dart` (line 138): replace bare counter string with ARB plural form using `AppLocalizations` `stationCount(n)` plural message. Add the ARB plural entry to `lib/l10n/*.arb` files for all supported locales. **Reqs:** S5-R5. **~20 lines (Dart) + ARB entries.**
- [ ] **T-S5-12** [GREEN] Edit `lib/widgets/tarjeta_emisora.dart` shimmer placeholders (lines 389-420): apply `BorderRadius` matching card corners. Edit `lib/pantallas/pantalla_buscar.dart` (lines 241-245): replace spinner with shimmer during loading state. **Reqs:** S5-R6. **~20 lines.**
- [ ] **T-S5-13** [GREEN] Edit `lib/pantallas/pantalla_ajustes.dart` icon sites (lines 985, 1028, 1031): replace non-`_rounded` icon variants with their `_rounded` equivalents. **Reqs:** S5-R7. **~5 lines.**
- [ ] **T-S5-14** [GREEN] Edit `lib/main.dart` (line 23) `AudioServiceConfig`: set `notificationColor` to `PluriWaveTokens.brandColor` (or equivalent token). **Reqs:** S5-R8. **~3 lines.**
- [x] **T-S5-07** [GREEN] ALL `Color(0x...)` literals outside `lib/tema/` removed (rg audit = 0 matches). New token DEFINITIONS in `PluriWaveTokens`: static `brand` (0xFF21D4D9, now referenced by `electricMagenta`), `brightCyan`, `auroraTeal`, `skyBlue`. Mapped: scaffold gradient → deepViolet/colorScheme.surface/auroraTeal; orbs → electricMagenta + colorScheme.secondary; alarmas glows → warmCoral/electricMagenta/skyBlue; visualizer gradient stop → warmCoral; premium orbs + tarjeta sweep → brightCyan. ALSO `Colors.grey` → onSurface(0.6), `Colors.green` → colorScheme.secondary, `Colors.white` overlays in reproductor → onSurface/onPrimary/glassBorder. **Reqs:** S5-R1. **DONE.**
- [x] **T-S5-08** [GREEN] Favorite button: `Semantics(container: true, button, toggled, label)` (container REQUIRED — see T-S5-01) + 48×48 target in both variants. `_AssetIcon` gained `semanticLabel` (decorative images get `excludeFromSemantics: true`); labels on alarm/vacation images + ringing-screen image via NEW keys `alarmIconLabel`/`vacationIconLabel`. **Reqs:** S5-R2. **DONE.**
- [x] **T-S5-09** [GREEN] `pluri_animate.dart` extended with `pluriFadeSlideIn`; ALL remaining `flutter_animate` call sites routed through PluriAnimate (pantalla_inicio chips + grid stagger, pantalla_buscar results, pantalla_reproductor 8 sites). Zero direct `.animate()` left in lib/. **Reqs:** S5-R3. **DONE.**
- [x] **T-S5-10** [GREEN] NEW `lib/l10n/formato_fechas.dart` `fechaCortaLocalizada(localeTag, fecha)`; `_fechaCorta(l10n, fecha)` delegates with `l10n.localeName` (all 6 call sites updated). **Reqs:** S5-R4. **DONE.**
- [x] **T-S5-11** [GREEN] Group counter → `l10n.stationCount(n)` ICU plural in ALL 13 locales (ru one/few/other, ar one/two/few/other, ja/zh/id other-only). Also NEW `streamUrlHint` key replacing the hardcoded `stream.ejemplo.com` hint. `flutter gen-l10n` regenerated. **Reqs:** S5-R5. **DONE.**
- [x] **T-S5-12** [GREEN] `TarjetaEmisoraShimmer` rounded (radiusLg image block, radius-6 text lines) + NEW `esCompacta` variant mirroring the search row layout; `PantallaBuscar` loading → 4 compact shimmer rows (pagination spinner untouched, out of S5-R6 scope). **Reqs:** S5-R6. **DONE.**
- [x] **T-S5-13** [GREEN] 9 icon sites → `_rounded` variants (radio_button_checked, equalizer, add_circle_outline, add, radio, play_arrow, delete_outline, favorite_outline, check_circle). `*_outlined`-family icons left as-is (no rounded-outline variant exists in Material). ALSO `_FormularioEmisora` sheet gained `useSafeArea`/`showDragHandle`. **Reqs:** S5-R7. **DONE.**
- [x] **T-S5-14** [GREEN] Config extracted to top-level `configuracionAudioService` const; `notificationColor: PluriWaveTokens.brand` (static const → usable in const context). **Reqs:** S5-R8. **DONE.**
### S5 verification
- [ ] **T-S5-15** Run `flutter test test/widgets/tarjeta_emisora_a11y_test.dart test/tema/pluri_animate_test.dart test/pantallas/pantalla_alarmas_fecha_test.dart test/pantallas/pantalla_favoritos_plural_test.dart`.
- [ ] **T-S5-16** Run `flutter test` (full suite) — no regressions.
- [ ] **T-S5-17** Run `flutter analyze`zero errors (no `Color(0x...)` in modified files beyond token definitions).
- [ ] **T-S5-18** Run `dart format` on all edited files.
- [x] **T-S5-15** Targeted run (6 files, 11 tests) — all green (RED captured first: `+0 -6`).
- [x] **T-S5-16** Full suite 121/121 (110 baseline + 11 new), no regressions.
- [x] **T-S5-17** `flutter analyze``No issues found!`; `rg 'Color(0x' lib` outside lib/tema + l10n/gen → ZERO matches.
- [x] **T-S5-18** `dart format` on all 20 touched Dart files (7 reflowed); analyze + suite re-run after format.
### S5 Definition of Done
- `flutter test` green.