fix(inicio): add the favourites count badge and plain-text Ver todas

Tier 4 visual fidelity, audit 1.11/1.12 (t4 line 80): a pill badge next
to "Tus emisoras" now shows the TOTAL favorite count (not the 8-capped
grid size), and "Ver todas" is plain 12px/w800 brand-teal text instead
of a Material TextButton with its own padding and splash.

Item 1.4 (hero art radius) was already fixed as a side effect of an
earlier commit — no change needed here.
This commit is contained in:
2026-07-30 12:36:27 +02:00
parent 56123ea51b
commit 163241d4a1
2 changed files with 136 additions and 10 deletions
+66 -10
View File
@@ -107,19 +107,46 @@ class _PantallaInicioState extends State<PantallaInicio> {
Row(
children: [
Expanded(
child: Text(
l10n.yourStationsTitle,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w900,
),
child: Row(
children: [
Flexible(
child: Text(
l10n.yourStationsTitle,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w900,
),
overflow: TextOverflow.ellipsis,
),
),
// Audit 1.11 (t4 line 80): a count badge next to the
// section title — the TOTAL favorite count, not the
// 8-capped grid size shown below it.
if (favoritos.isNotEmpty) ...[
const SizedBox(width: 10),
_InsigniaRecuento(cuenta: favoritos.length),
],
],
),
),
TextButton(
onPressed:
() => context.read<EstadoNavegacionRaiz>().irA(
RaizPluriWave.favoritos,
// Audit 1.12 (t4 line 80): "Ver todas" is plain 12px/w800
// brand-teal text — not a Material TextButton with its own
// padding and splash.
Semantics(
button: true,
child: GestureDetector(
onTap:
() => context.read<EstadoNavegacionRaiz>().irA(
RaizPluriWave.favoritos,
),
child: Text(
l10n.seeAllAction,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w800,
color: context.pluriTokens.electricMagenta,
),
child: Text(l10n.seeAllAction),
),
),
),
],
),
@@ -574,6 +601,35 @@ String? _metaEscuchar(Emisora emisora) {
return partes.isEmpty ? null : partes.join(' · ');
}
/// Audit 1.11 (t4 line 80): the pill badge next to "Tus emisoras" showing
/// the total favorite count — `rgba(255,255,255,.08)` fill, 11px/w800/60%.
class _InsigniaRecuento extends StatelessWidget {
const _InsigniaRecuento({required this.cuenta});
final int cuenta;
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(999),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
child: Text(
'$cuenta',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w800,
color: const Color(0xFFF2F7FA).withValues(alpha: 0.6),
),
),
),
);
}
}
/// Audit 1.10 (t4 lines 82-88): the "Tus emisoras" grid cell — a 44px
/// square thumbnail (radius 11), name (13/w700/lh1.2) and genre
/// (11/55%). Deliberately NOT `TarjetaEmisora(esCompacta: true)`: that
+70
View File
@@ -334,6 +334,76 @@ void main() {
expect(find.byKey(const ValueKey('escuchar-hero-meta')), findsNothing);
},
);
testWidgets('visual fidelity (audit 1.11): a count badge sits next to "Tus '
'emisoras", showing the TOTAL favorite count, not the 8-capped grid '
'size', (tester) async {
_setLargeSurfaceSize(tester);
final favoritos = FakeServicioFavoritos();
final estado = EstadoRadio(
audio: FakeServicioAudio(),
favoritos: favoritos,
radio: FakeServicioRadio(),
servicioEcualizador: FakeServicioEcualizador(),
servicioGrabacion: FakeServicioGrabacionRadio(),
resolverArchivoCustom: _archivoCustomVacio,
iniciarAutomaticamente: false,
);
addTearDown(estado.dispose);
await tester.runAsync(estado.inicializar);
for (var i = 0; i < 11; i++) {
await favoritos.agregar(
emisoraDemo(uuid: 'badge-$i', nombre: 'Badge Estacion $i'),
);
}
await estado.cargarFavoritos();
await tester.pumpWidget(
_conProviders(estado, _testApp(const PantallaInicio())),
);
await _pumpStableFrame(tester);
expect(
find.text('11'),
findsOneWidget,
reason:
'prototype t4 line 80: the badge reflects how many favorites '
'exist, even though the grid below caps display at 8',
);
});
testWidgets(
'visual fidelity (audit 1.12): "Ver todas" renders as plain brand-teal '
'text, not a Material TextButton',
(tester) async {
_setLargeSurfaceSize(tester);
final favoritos = FakeServicioFavoritos();
final estado = EstadoRadio(
audio: FakeServicioAudio(),
favoritos: favoritos,
radio: FakeServicioRadio(),
servicioEcualizador: FakeServicioEcualizador(),
servicioGrabacion: FakeServicioGrabacionRadio(),
resolverArchivoCustom: _archivoCustomVacio,
iniciarAutomaticamente: false,
);
addTearDown(estado.dispose);
await tester.runAsync(estado.inicializar);
await favoritos.agregar(emisoraDemo(uuid: 'f1', nombre: 'Favorita Uno'));
await estado.cargarFavoritos();
await tester.pumpWidget(
_conProviders(estado, _testApp(const PantallaInicio())),
);
await _pumpStableFrame(tester);
expect(find.byType(TextButton), findsNothing);
final texto = tester.widget<Text>(find.text('Ver todas'));
expect(texto.style?.fontSize, 12);
expect(texto.style?.fontWeight, FontWeight.w800);
expect(texto.style?.color, const Color(0xFF21D4D9));
},
);
}
/// Mirrors the app.dart wiring: EstadoRadio owns the domain notifiers and