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:
@@ -19,9 +19,15 @@ class PantallaFavoritos extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final estado = context.watch<EstadoRadio>();
|
||||
final favoritos = estado.listaFavoritos;
|
||||
final grupos = estado.gruposFavoritos;
|
||||
// S4-R5: no root watch — select only the fields this screen reads. The
|
||||
// getters are identity-memoized, so playback notifications that do not
|
||||
// change favorites/groups no longer rebuild the screen.
|
||||
final favoritos = context.select<EstadoRadio, List<Emisora>>(
|
||||
(e) => e.listaFavoritos,
|
||||
);
|
||||
final grupos = context.select<EstadoRadio, List<GrupoFavoritos>>(
|
||||
(e) => e.gruposFavoritos,
|
||||
);
|
||||
final l10n = AppLocalizations.of(context);
|
||||
|
||||
if (favoritos.isEmpty) {
|
||||
@@ -49,16 +55,17 @@ class PantallaFavoritos extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
final gruposVisibles = grupos.isEmpty
|
||||
? [
|
||||
GrupoFavoritos(
|
||||
id: GrupoFavoritos.sinAsignarId,
|
||||
nombre: l10n.favoriteGroupsUnassigned,
|
||||
orden: 0,
|
||||
protegido: true,
|
||||
),
|
||||
]
|
||||
: grupos;
|
||||
final gruposVisibles =
|
||||
grupos.isEmpty
|
||||
? [
|
||||
GrupoFavoritos(
|
||||
id: GrupoFavoritos.sinAsignarId,
|
||||
nombre: l10n.favoriteGroupsUnassigned,
|
||||
orden: 0,
|
||||
protegido: true,
|
||||
),
|
||||
]
|
||||
: grupos;
|
||||
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
@@ -86,9 +93,10 @@ class PantallaFavoritos extends StatelessWidget {
|
||||
_GrupoFavoritosPanel(
|
||||
grupo: grupo,
|
||||
grupos: gruposVisibles,
|
||||
emisoras: favoritos
|
||||
.where((e) => e.grupoFavoritosId == grupo.id)
|
||||
.toList(),
|
||||
emisoras:
|
||||
favoritos
|
||||
.where((e) => e.grupoFavoritosId == grupo.id)
|
||||
.toList(),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
@@ -125,7 +133,9 @@ class _GrupoFavoritosPanel extends StatelessWidget {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(grupo.esSinAsignar ? Icons.lock_rounded : Icons.folder_rounded),
|
||||
Icon(
|
||||
grupo.esSinAsignar ? Icons.lock_rounded : Icons.folder_rounded,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
@@ -181,30 +191,31 @@ class _FavoritoItem extends StatelessWidget {
|
||||
final seleccionado = await showModalBottomSheet<String>(
|
||||
context: context,
|
||||
showDragHandle: true,
|
||||
builder: (ctx) => SafeArea(
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 4, 20, 12),
|
||||
child: Text(
|
||||
l10n.favoriteGroupsAssign,
|
||||
style: Theme.of(ctx).textTheme.titleLarge,
|
||||
),
|
||||
),
|
||||
for (final grupo in grupos)
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
grupo.id == emisora.grupoFavoritosId
|
||||
? Icons.radio_button_checked_rounded
|
||||
: Icons.radio_button_off_rounded,
|
||||
builder:
|
||||
(ctx) => SafeArea(
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 4, 20, 12),
|
||||
child: Text(
|
||||
l10n.favoriteGroupsAssign,
|
||||
style: Theme.of(ctx).textTheme.titleLarge,
|
||||
),
|
||||
),
|
||||
title: Text(_nombreVisible(l10n, grupo)),
|
||||
onTap: () => Navigator.pop(ctx, grupo.id),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
for (final grupo in grupos)
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
grupo.id == emisora.grupoFavoritosId
|
||||
? Icons.radio_button_checked_rounded
|
||||
: Icons.radio_button_off_rounded,
|
||||
),
|
||||
title: Text(_nombreVisible(l10n, grupo)),
|
||||
onTap: () => Navigator.pop(ctx, grupo.id),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
if (seleccionado == null || !context.mounted) return;
|
||||
await context.read<EstadoRadio>().asignarGrupoFavorito(
|
||||
|
||||
Reference in New Issue
Block a user