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:
@@ -107,19 +107,46 @@ class _PantallaInicioState extends State<PantallaInicio> {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Row(
|
||||||
l10n.yourStationsTitle,
|
children: [
|
||||||
style: theme.textTheme.titleMedium?.copyWith(
|
Flexible(
|
||||||
fontWeight: FontWeight.w900,
|
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(
|
// Audit 1.12 (t4 line 80): "Ver todas" is plain 12px/w800
|
||||||
onPressed:
|
// brand-teal text — not a Material TextButton with its own
|
||||||
() => context.read<EstadoNavegacionRaiz>().irA(
|
// padding and splash.
|
||||||
RaizPluriWave.favoritos,
|
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(' · ');
|
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
|
/// 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
|
/// square thumbnail (radius 11), name (13/w700/lh1.2) and genre
|
||||||
/// (11/55%). Deliberately NOT `TarjetaEmisora(esCompacta: true)`: that
|
/// (11/55%). Deliberately NOT `TarjetaEmisora(esCompacta: true)`: that
|
||||||
|
|||||||
@@ -334,6 +334,76 @@ void main() {
|
|||||||
expect(find.byKey(const ValueKey('escuchar-hero-meta')), findsNothing);
|
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
|
/// Mirrors the app.dart wiring: EstadoRadio owns the domain notifiers and
|
||||||
|
|||||||
Reference in New Issue
Block a user