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
+21 -16
View File
@@ -3,6 +3,9 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pluriwave/estado/estado_busqueda.dart';
import 'package:pluriwave/estado/estado_ecualizador.dart';
import 'package:pluriwave/estado/estado_grabacion.dart';
import 'package:pluriwave/estado/estado_radio.dart';
import 'package:pluriwave/l10n/gen/app_localizations.dart';
import 'package:pluriwave/pantallas/pantalla_favoritos.dart';
@@ -41,10 +44,7 @@ void main() {
await tester.runAsync(estado.inicializar);
await tester.pumpWidget(
ChangeNotifierProvider<EstadoRadio>.value(
value: estado,
child: _testApp(const PantallaInicio()),
),
_conProviders(estado, _testApp(const PantallaInicio())),
);
await _pumpStableFrame(tester);
@@ -109,10 +109,7 @@ void main() {
await tester.runAsync(estado.inicializar);
await tester.pumpWidget(
ChangeNotifierProvider<EstadoRadio>.value(
value: estado,
child: _testApp(const PantallaInicio()),
),
_conProviders(estado, _testApp(const PantallaInicio())),
);
await _pumpStableFrame(tester);
@@ -153,10 +150,7 @@ void main() {
await tester.runAsync(estado.inicializar);
await tester.pumpWidget(
ChangeNotifierProvider<EstadoRadio>.value(
value: estado,
child: _testApp(const PantallaInicio()),
),
_conProviders(estado, _testApp(const PantallaInicio())),
);
await _pumpStableFrame(tester);
@@ -176,10 +170,7 @@ void main() {
await _pumpStableFrame(tester);
await tester.pumpWidget(
ChangeNotifierProvider<EstadoRadio>.value(
value: estado,
child: _testApp(const PantallaFavoritos()),
),
_conProviders(estado, _testApp(const PantallaFavoritos())),
);
await _pumpStableFrame(tester);
@@ -188,6 +179,20 @@ void main() {
});
}
/// Mirrors the app.dart wiring: EstadoRadio owns the domain notifiers and
/// the providers only expose the instances (no dispose callbacks).
Widget _conProviders(EstadoRadio estado, Widget child) {
return MultiProvider(
providers: [
ChangeNotifierProvider<EstadoRadio>.value(value: estado),
ListenableProvider<EstadoEcualizador>.value(value: estado.ecualizador),
ListenableProvider<EstadoGrabacion>.value(value: estado.grabacion),
ListenableProvider<EstadoBusqueda>.value(value: estado.busqueda),
],
child: child,
);
}
Widget _testApp(Widget body) {
return MaterialApp(
locale: const Locale('es'),