feat: Implement startup retry mechanism for custom stations and equalizer persistence

- Added state management for startup retry and custom station handling in `EstadoRadio`.
- Created tasks for implementing strict TDD with RED tests for HTTP failure retries and EQ persistence.
- Developed verification report to ensure compliance with TDD practices.
- Introduced fake services for testing, including `FakeServicioAudio`, `FakeServicioFavoritos`, and `FakeServicioRadio`.
- Implemented widget tests for `PantallaInicio` and `PantallaFavoritos` to validate UI behavior with custom stations.
- Enhanced `ServicioRadio` to support host rotation and retry logic for API calls.
- Established a new configuration file to enforce project constraints and testing rules.
This commit is contained in:
Javier Bautista Fernández
2026-04-27 17:34:04 +02:00
parent 922b3b4859
commit d579a0e107
21 changed files with 1902 additions and 156 deletions

View File

@@ -44,6 +44,11 @@ class _SeccionEcualizador extends StatelessWidget {
return Consumer<EstadoRadio>(
builder: (ctx, estado, _) {
final disponible = estado.ecualizadorDisponible;
final emisoraActual = estado.emisoraActual;
final mostrarModoPorEmisora =
emisoraActual != null && estado.emisoraActualEsFavorita;
final usandoEqPropio = estado.emisoraActualTienePresetPropio;
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
@@ -57,27 +62,41 @@ class _SeccionEcualizador extends StatelessWidget {
const Spacer(),
if (!disponible)
Chip(
label: const Text('Reproduce una emisora para activar'),
label: const Text('Se guarda aunque no esté activo'),
visualDensity: VisualDensity.compact,
),
],
),
if (disponible) ...[
if (mostrarModoPorEmisora) ...[
const SizedBox(height: 8),
PresetsEcualizadorWidget(
presetActual: estado.presetEcualizador,
onSeleccionar: (p) => estado.cambiarPresetEcualizador(p),
),
const SizedBox(height: 12),
EcualizadorWidget(
preset: estado.presetEcualizador,
onCambio: (p) {
for (int i = 0; i < p.bandas.length; i++) {
estado.cambiarBandaEcualizador(i, p.bandas[i]);
}
SwitchListTile.adaptive(
contentPadding: EdgeInsets.zero,
title: const Text('Usar EQ propio para esta favorita'),
subtitle: Text(
usandoEqPropio
? 'Activo para ${emisoraActual.nombre}'
: 'Usando EQ principal para ${emisoraActual.nombre}',
),
value: usandoEqPropio,
onChanged: (usarPropio) {
estado.cambiarModoEcualizadorEmisoraActual(
usarPropio: usarPropio,
);
},
),
],
const SizedBox(height: 8),
PresetsEcualizadorWidget(
presetActual: estado.presetEcualizador,
onSeleccionar: (p) => estado.cambiarPresetEcualizador(p),
),
const SizedBox(height: 12),
EcualizadorWidget(
preset: estado.presetEcualizador,
onCambio: (p) {
estado.cambiarPresetEcualizador(p);
},
),
],
),
);