feat(reproductor): restructure full player with tool-tray and EQ sheet

Restructure pantalla_reproductor.dart onto PluriPushScaffold (design
ADR-2 - this screen is the documented single consumer of titleOverride,
a centered live/not-playing status pill, and the non-default
keyboard_arrow_down leadingIcon). Square art replaces the old circular
hero, favorite moves from the AppBar into the transport row (the
redundant live-indicator dot is dropped - the AppBar pill already covers
that signal), the old separate info chips collapse into a single
subtitle line, and a new quality row surfaces codec/bitrate with a
"Cambiar" action that reconnects the current stream (this app has no
per-station alternate-quality capability to invoke, so this reuses the
same reproducir() call the existing error-state retry button already
uses, rather than a dead button or an invented picker).

The always-expanded recording panel and the standalone sleep-timer
button both become tool-tray tiles (EQ propio / Grabar / sleep timer /
Compartir), each opening its own bottom sheet. "EQ propio" opens a sheet
hosting EcualizadorWidget - the exact same component WU13 restyled for
Settings, bound via the existing presetParaEmisora/guardarPresetPorEmisora
per-station persistence path. No second editor was created; the
multi-device-eq resolution hierarchy is untouched.

pantalla_reproductor.dart had zero test coverage before this commit (907
lines) - writing it first surfaced two pre-existing bugs blocking any
coverage at all, both fixed: initState called estado.reproducir()
directly, which notifies listeners synchronously before its first await
and threw "setState() during build" the instant the screen mounted
against a fresh Provider tree (fixed via addPostFrameCallback); and the
body Column had no scrollable ancestor and overflowed even a generously
tall viewport (fixed by wrapping it in a SingleChildScrollView, a real
UX improvement and not just a test workaround).

The three protected EQ test files (servicio_ecualizador_test.dart,
estado_ecualizador_test.dart, servicio_audio_eq_reapply_test.dart) stay
unmodified. Full suite: 730/730 green (2 skipped, unchanged), up from 713.

size:exception - realized 1,410 changed lines (25 files including this
docs update) against the 450-600 forecast: the restructured screen file
alone is 658 lines (a near-total rewrite of a 907-line file, not a
patch), its new test file (first-ever coverage) is 519 lines, and a new
test fake plus a togglePlay() override account for the rest. Not
splittable: the restructure, the tool tray, and the EQ-sheet wiring are
one cohesive change to one screen.
This commit is contained in:
2026-07-29 14:20:27 +02:00
parent c9fe0ad651
commit dc21732027
20 changed files with 1178 additions and 242 deletions
+59 -16
View File
@@ -52,7 +52,7 @@
| 10 | `feat(alarmas): rewrite alarm editor with inline time widget` | 8 | 500-650 | High | **Yes — indivisible new widget**‡ |
| 11 | `feat(alarma-sonando): restyle ringing screen, drop live countdown label` | 1 | 200-300 | Low (safety-critical review attention: High) | No |
| 13 | `feat(eq): restyle equalizer screen and add custom presets` | 3a | ~~400-550~~**REALIZED: 1,871** (1,793+ / 78-, 25 files) | Medium-High | **Yes — retroactive, see WU13 section** |
| 14 | `feat(reproductor): restructure full player with tool-tray and EQ sheet` | 13 | 450-600 | Medium-High | Monitor |
| 14 | `feat(reproductor): restructure full player with tool-tray and EQ sheet` | 13 | ~~450-600~~**REALIZED: 1,335** (1,114+ / 221-, 19 files) | Medium-High | **Yes — retroactive, see WU14 section** |
| 15 | `feat(grabaciones): add recordings library screen` | 3b | ~~300-400~~**REALIZED: 1,767** (1,767+ / 0-, 22 files) | Medium | Monitor§ |
| 15b | `fix(grabaciones): wire the recordings library into Settings navigation` | 15 | 60-100 | Low | No |
| 16 | `feat(connectivity): restyle offline and reconnect banners` | 1 | 150-250 | Low | No |
@@ -807,24 +807,67 @@ Per-Station EQ Entry Relocates, Resolution Logic Does Not
**Modified tests**: `pantalla_reproductor_test.dart` (or equivalent widget test). Three EQ test files must pass
**unmodified**.
- [ ] 14.1 RED — the per-station EQ bottom sheet renders **the same `EcualizadorWidget` type** WU13 restyled
- [x] 14.1 RED — the per-station EQ bottom sheet renders **the same `EcualizadorWidget` type** WU13 restyled
(assert by `runtimeType`, so a duplicate implementation fails the test, not just a visual review).
- [ ] 14.2 RED — the 4 tool-tray tiles ("EQ propio", "Grabar", sleep-timer value, "Compartir") each open their own
bottom sheet.
- [ ] 14.3 RED — opening "EQ propio" for a playing station shows 5 sliders bound to that station's resolved preset,
- [x] 14.2 RED — the 4 tool-tray tiles ("EQ propio", "Grabar", sleep-timer value, "Compartir") each open their own
bottom sheet (Compartir invokes directly — there is no sheet content for a share action).
- [x] 14.3 RED — opening "EQ propio" for a playing station shows 5 sliders bound to that station's resolved preset,
and a change round-trips through the existing per-station persistence path (`presetsPorEmisora` /
`presetsMatriz`).
- [ ] 14.4 GREEN — restructure `pantalla_reproductor.dart`: square art, favorite moved into the transport row,
single subtitle line (collapse the current `_InfoChips` `Wrap`), 4-tile tool-tray grid replacing the
always-expanded recording panel + separate sleep-timer button + EQ toggle.
- [ ] 14.5 GREEN — wire "EQ propio" to a bottom sheet hosting `EcualizadorWidget` bound via
`EstadoEcualizador.presetParaEmisora(uuid)` / `guardarPresetPorEmisora(uuid, ...)`.
- [ ] 14.6 GREEN — add the quality row + "Cambiar" action and the "Compartir" tool-tray tile.
- [ ] 14.7 REFACTOR — confirm no second EQ editor file was created; confirm the `multi-device-eq` regression
scenarios (device-event resolve-and-apply, first-seen bootstrap, cold start, connect/disconnect/reconnect,
toggle-off) still pass unmodified.
- [ ] 14.8 Verify — the 3 EQ test files remain green and unmodified; `EcualizadorWidget` type-identity assertion
passes.
- [x] 14.4 GREEN — restructured `pantalla_reproductor.dart` onto `PluriPushScaffold` (design ADR-2 — this screen is
the documented single consumer of `titleOverride`, a centered live/not-playing `PluriStatusPill`, and of the
non-default `leadingIcon: keyboard_arrow_down_rounded`): square art (`ClipRRect`, was `ClipOval`), favorite
moved into the transport row (the old redundant live-indicator dot removed — the AppBar pill already covers
that signal, so "favorite / stop / play-pause" matches the proposal's own "3 controles" note), single
subtitle line (collapses the old `_InfoChips` `Wrap`; codec/bitrate moved to the new quality row), 4-tile
tool-tray row replacing the always-expanded recording panel + separate sleep-timer button + EQ toggle. Body
wrapped in `SingleChildScrollView` (was a fixed `Column` + `Spacer()`, which overflowed even a generously
tall test viewport — see 2 bugfixes below).
- [x] 14.5 GREEN — wired "EQ propio" to a bottom sheet hosting `EcualizadorWidget` bound via
`EstadoEcualizador.presetParaEmisora(uuid)` / `guardarPresetPorEmisora(uuid, ...)`. Deliberately just the 5
sliders, no preset-chip row — the spec scenario's own wording is "5 sliders", and ADR-5's wiring table only
names `EcualizadorWidget` for this consumer.
- [x] 14.6 GREEN — added the quality row + "Cambiar" action and the "Compartir" tool-tray tile. **Design decision,
not specified by any ADR (WU14 has none)**: this app has no per-station alternate-quality capability to
invoke (a Radio Browser station is one fixed stream) — "Cambiar" reconnects the current stream (the SAME
`estado.reproducir(emisora)` call the existing error-state "Retry" button already uses) instead of a dead
button or an invented picker, matching this branch's "don't invent a capability absent from the domain"
discipline (WU5 per-station artwork, WU9 dashed border). "Compartir" mirrors WU15's injectable `compartir`
constructor-parameter pattern (defaults to the real `share_plus` call), sharing the station name + stream url.
- [x] 14.7 REFACTOR — confirmed no second EQ editor file was created (`grep`-equivalent: only one `EcualizadorWidget`
class exists, in `lib/widgets/ecualizador_widget.dart`, imported and reused here); confirmed the
`multi-device-eq` regression scenarios in `estado_ecualizador_test.dart`'s "4-level resolution (Phase 5)"
group still pass unmodified (this WU never touches `EstadoEcualizador`'s resolution logic, only calls its
EXISTING `presetParaEmisora`/`guardarPresetPorEmisora` methods). Removed dead code found during the
restructure: `_pulseController` (an `AnimationController` created and disposed but never actually driven by
anything) and the `SingleTickerProviderStateMixin` it required.
- [x] 14.8 Verify — the 3 EQ test files remain green and **unmodified**; `EcualizadorWidget` type-identity assertion
passes (both via `find.byType` and a `runtimeType`-predicate structural regression guard). Full suite:
730/730 green (2 skipped, unchanged), up from 713.
**Two pre-existing bugs found and fixed, surfaced by writing this screen's first-ever test coverage** (both
directly blocked test coverage from working at all, so neither could be deferred):
1. `initState` called `estado.reproducir(...)` directly, which calls `notifyListeners()` **synchronously** before
its first `await` when no recording needs stopping — threw "setState() or markNeedsBuild() called during
build" the instant this screen mounted against a fresh Provider tree. Fixed via `WidgetsBinding.instance.
addPostFrameCallback`.
2. The body `Column` had no scrollable ancestor and overflowed the default 800x600 test viewport (and would
overflow on a genuinely short real device too, given the content: hero, name, subtitle, quality row, visualizer,
transport, tool tray). Fixed by wrapping the body in `SingleChildScrollView` (see 14.4) — a real UX improvement,
not just a test workaround.
**`size:exception` recorded.** Realized: **1,335 changed lines** (1,114+/221-) across 19 files against the
450-600 forecast — same "a strict-TDD commit carries its test files" pattern as every prior WU (Engram
`reference/estimating-strict-tdd-diffs`, id 2514), though smaller this time since only 2 new ARB keys were needed
(`playerToolEqLabel`, `playerQualityChangeAction` — everything else reused existing keys: `recordAction`,
`recordingActiveTitle`, `sleepTimer`, `recordingActionShare`, `liveNow`, `notPlaying`, `qualityOriginal`,
`qualityUnknown`). Breakdown: `lib/pantallas/pantalla_reproductor.dart` alone is 658 lines (a near-total
restructure of a 907-line file, not a small patch); the new `pantalla_reproductor_test.dart` (writing coverage for
a file that had ZERO before this commit, per this WU's own explicit mandate) is 519 lines; `test/helpers/fakes.dart`
gained 64 (new `FakeServicioGrabacionRadioActivable` plus a `togglePlay()` override the play/pause characterization
test needed); the rest is the 2-key ARB/l10n-gen cascade. Not splittable: the restructure, the tool tray, and the
EQ-sheet wiring are one cohesive change to one screen — a split would leave either an unstyled screen with a tool
tray that has nothing to open, or a tool tray with no restructured screen to live in.
## WU15 — Grabaciones library (new list)