refactor(state): extract recording and search state, scope screen rebuilds

- New EstadoGrabacion owns the recording service, subscription, directory/size preferences and open-file actions
- New EstadoBusqueda owns search, nearby stations, pagination and the min-bitrate filter
- New orden_emisoras.dart with the OrdenEmisoras enum, shared sorter and list identity memoization so context.select comparisons work on derived lists
- Large screens (inicio, buscar, favoritos, ajustes, reproductor) consume scoped selects/dedicated notifiers instead of root context.watch<EstadoRadio>, so audio buffer events no longer rebuild whole screens
- Remove all 15 TODO(S4b) compat members from EstadoRadio; consumers use the dedicated providers. EstadoRadio drops from ~1121 to 753 lines, keeping playback/stations/favorites orchestration
- 8 new tests including a rebuild-scoping probe (110 total green), flutter analyze clean
This commit is contained in:
2026-06-11 21:43:18 +02:00
parent 0416b301b2
commit 52855e75c2
17 changed files with 1195 additions and 643 deletions
+23 -53
View File
@@ -60,7 +60,7 @@ void main() {
await estado.inicializar();
await estado.reproducir(emisora);
expect(estado.presetEcualizador, principal);
expect(estado.ecualizador.presetActual, principal);
expect(audio.presetsAplicados.first, principal);
expect(audio.presetsAplicados.last, principal);
},
@@ -85,11 +85,11 @@ void main() {
await estado.inicializar();
expect(estado.ecualizadorDisponible, isFalse);
expect(estado.presetEcualizador, principal);
expect(estado.presetPrincipalEcualizador, principal);
expect(estado.ecualizador.disponible, isFalse);
expect(estado.ecualizador.presetActual, principal);
expect(estado.ecualizador.presetPrincipal, principal);
expect(
estado.presetEcualizadorPorEmisora('fav-1'),
estado.ecualizador.presetPorEmisora('fav-1'),
PresetEcualizador.rock,
);
},
@@ -151,15 +151,15 @@ void main() {
await estado.inicializar();
await estado.cargarFavoritos();
await estado.guardarPresetEcualizadorPorEmisora(emisora.uuid, propio);
await estado.ecualizador.guardarPresetPorEmisora(emisora.uuid, propio);
await estado.reproducir(emisora);
expect(estado.presetEcualizador, propio);
expect(estado.ecualizador.presetActual, propio);
expect(audio.presetsAplicados.last, propio);
await estado.deshabilitarPresetEcualizadorPorEmisora(emisora.uuid);
await estado.ecualizador.deshabilitarPresetPorEmisora(emisora.uuid);
await estado.reproducir(emisora);
expect(estado.presetEcualizador, principal);
expect(estado.ecualizador.presetActual, principal);
expect(audio.presetsAplicados.last, principal);
},
);
@@ -186,8 +186,8 @@ void main() {
await estado.cargarFavoritos();
await estado.reproducir(emisora);
expect(estado.tienePresetEcualizadorPorEmisora(emisora.uuid), isFalse);
expect(estado.presetEcualizador, principal);
expect(estado.ecualizador.tienePresetPorEmisora(emisora.uuid), isFalse);
expect(estado.ecualizador.presetActual, principal);
expect(audio.presetsAplicados.last, principal);
},
);
@@ -207,15 +207,15 @@ void main() {
);
await estado.inicializar();
expect(estado.ecualizadorActivo, isTrue);
expect(estado.ecualizador.activo, isTrue);
await estado.cambiarEcualizadorActivo(false);
expect(estado.ecualizadorActivo, isFalse);
await estado.ecualizador.cambiarActivo(false);
expect(estado.ecualizador.activo, isFalse);
expect(servicioEcualizador.config.activo, isFalse);
expect(audio.cambiosEcualizadorActivo.last, isFalse);
await estado.cambiarEcualizadorActivo(true);
expect(estado.ecualizadorActivo, isTrue);
await estado.ecualizador.cambiarActivo(true);
expect(estado.ecualizador.activo, isTrue);
expect(servicioEcualizador.config.activo, isTrue);
expect(audio.cambiosEcualizadorActivo.last, isTrue);
},
@@ -285,11 +285,11 @@ void main() {
);
await estado.inicializar();
await estado.guardarPresetEcualizadorPorEmisora(
await estado.ecualizador.guardarPresetPorEmisora(
primera.uuid,
PresetEcualizador.rock,
);
await estado.guardarPresetEcualizadorPorEmisora(
await estado.ecualizador.guardarPresetPorEmisora(
segunda.uuid,
PresetEcualizador.jazz,
);
@@ -301,7 +301,7 @@ void main() {
audio.completar(primera.uuid);
await primeraFuture;
expect(estado.presetEcualizador, PresetEcualizador.jazz);
expect(estado.ecualizador.presetActual, PresetEcualizador.jazz);
expect(radio.ultimoUuidClick, segunda.uuid);
},
);
@@ -319,32 +319,8 @@ void main() {
expect(lista.map((e) => e.orden).toList(), equals([0, 1, 2]));
});
test('cargarMasBusqueda pagina resultados y acota memoria', () async {
final emisoras = List.generate(
70,
(i) => emisoraDemo(uuid: 'page-$i', nombre: 'Page $i'),
);
final estado = EstadoRadio(
audio: FakeServicioAudio(),
favoritos: FakeServicioFavoritos(),
radio: FakeServicioRadio(busqueda: emisoras),
servicioEcualizador: FakeServicioEcualizador(),
resolverArchivoCustom: _archivoCustomVacio,
iniciarAutomaticamente: false,
);
await estado.inicializar();
await estado.buscar(nombre: 'page');
expect(estado.resultadosBusqueda, hasLength(30));
expect(estado.hayMasBusqueda, isTrue);
await estado.cargarMasBusqueda();
expect(estado.resultadosBusqueda, hasLength(60));
await estado.cargarMasBusqueda();
expect(estado.resultadosBusqueda, hasLength(70));
expect(estado.hayMasBusqueda, isFalse);
});
// The search pagination test moved to test/estado/estado_busqueda_test.dart
// (S4-R3: search state extracted to EstadoBusqueda).
test('toggleFavorito refresca lista global y evita estado stale', () async {
final favoritos = FakeServicioFavoritos();
@@ -388,16 +364,10 @@ void main() {
final grupo = estado.gruposFavoritos.last;
await estado.asignarGrupoFavorito(emisora.uuid, grupo.id);
expect(
estado.listaFavoritos.first.grupoFavoritosId,
grupo.id,
);
expect(estado.listaFavoritos.first.grupoFavoritosId, grupo.id);
await estado.eliminarGrupoFavoritos(grupo.id);
expect(
estado.listaFavoritos.first.grupoFavoritosId,
'sin_asignar',
);
expect(estado.listaFavoritos.first.grupoFavoritosId, 'sin_asignar');
});
});
}