From fc866d7ec9f7a78e3fcac9ed6ec21138486699d2 Mon Sep 17 00:00:00 2001 From: freetlab Date: Thu, 30 Jul 2026 20:18:59 +0200 Subject: [PATCH] fix(buscar): correct the gaps between the filter row and the results Issue 3 (partial): the results area had no top gap against the filter row in one state and reused the horizontal constant for a vertical axis in another. Applies the 3-tier scale properly -- row tier for background-less placeholders, card tier for card states. The rest of the app's spacing review is still outstanding. --- lib/pantallas/pantalla_buscar.dart | 34 ++++++++++- test/pantallas/pantalla_buscar_test.dart | 73 ++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 3 deletions(-) diff --git a/lib/pantallas/pantalla_buscar.dart b/lib/pantallas/pantalla_buscar.dart index 940ab34..fec8a65 100644 --- a/lib/pantallas/pantalla_buscar.dart +++ b/lib/pantallas/pantalla_buscar.dart @@ -543,8 +543,17 @@ class _PantallaBuscarState extends State { if (estado.cargando) { // S5-R6: shimmer placeholders instead of a bare spinner, consistent // with the loading pattern used by the home grid. + // Issue 3 (feedback-pruebas): row tier (12), not card tier (16) -- + // these are background-less row placeholders, same tier as the real + // results below; the top inset is the standard section gap rather + // than the horizontal constant reused for a vertical axis. return Padding( - padding: const EdgeInsets.all(PluriLayout.horizontal), + padding: const EdgeInsets.fromLTRB( + PluriLayout.rowHorizontal, + PluriLayout.sectionGap, + PluriLayout.rowHorizontal, + PluriLayout.rowHorizontal, + ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -579,8 +588,17 @@ class _PantallaBuscarState extends State { // across the app -- favorites, the discovery grid -- which this // item does not touch). final query = _controller.text.trim(); + // Issue 3 (feedback-pruebas): this card-tier state had NO top gap at + // all against the filter row above it -- the standard section gap + // now matches the other two mutually-exclusive results-area states + // (loading, populated) above. return Padding( - padding: const EdgeInsets.symmetric(horizontal: PluriLayout.horizontal), + padding: const EdgeInsets.fromLTRB( + PluriLayout.horizontal, + PluriLayout.sectionGap, + PluriLayout.horizontal, + 0, + ), child: _TarjetaSinResultados( titulo: sinFiltros @@ -616,7 +634,17 @@ class _PantallaBuscarState extends State { return ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), - padding: const EdgeInsets.all(PluriLayout.horizontal), + // Issue 3 (feedback-pruebas): row tier (12), not card tier (16) -- + // `FilaEmisoraPlana` rows are documented as "flat, background-less" + // (audit 6.5) but this padding never got updated to match when Tier 1 + // introduced the 3-tier scale. The top inset is the standard section + // gap, not the horizontal constant reused for a vertical axis. + padding: const EdgeInsets.fromLTRB( + PluriLayout.rowHorizontal, + PluriLayout.sectionGap, + PluriLayout.rowHorizontal, + PluriLayout.rowHorizontal, + ), itemCount: total, itemBuilder: (context, i) { if (i >= resultados.length) { diff --git a/test/pantallas/pantalla_buscar_test.dart b/test/pantallas/pantalla_buscar_test.dart index 5d418be..5358510 100644 --- a/test/pantallas/pantalla_buscar_test.dart +++ b/test/pantallas/pantalla_buscar_test.dart @@ -15,6 +15,7 @@ import 'package:pluriwave/servicios/servicio_audio.dart'; import 'package:pluriwave/tema/pluriwave_theme.dart'; import 'package:pluriwave/tema/pluriwave_tokens.dart'; import 'package:pluriwave/widgets/fila_emisora_plana.dart'; +import 'package:pluriwave/widgets/pluri_layout.dart'; import 'package:pluriwave/widgets/tarjeta_emisora.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -835,6 +836,78 @@ void main() { ); }); + testWidgets('issue 3 (feedback-pruebas): the results list uses row-tier ' + 'horizontal padding (12), not the card-tier constant this "flat, ' + 'background-less row" was documented as needing but never got', ( + tester, + ) async { + _setLargeSurfaceSize(tester); + final estado = _crearEstado( + radio: FakeServicioRadio( + busqueda: [emisoraDemo(uuid: 'r-1', nombre: 'Radio Uno')], + ), + ); + addTearDown(estado.dispose); + await tester.runAsync(estado.inicializar); + + await tester.pumpWidget(_conProviders(estado, _testApp())); + await _pumpStableFrame(tester); + await tester.enterText(find.byType(SearchBar), 'radio'); + await tester.testTextInput.receiveAction(TextInputAction.done); + await _pumpStableFrame(tester); + + final fila = find.byType(FilaEmisoraPlana); + expect( + tester.getTopLeft(fila).dx, + PluriLayout.rowHorizontal, + reason: + 'issue 3: background-less rows are row tier (12), not card ' + 'tier (16)', + ); + }); + + testWidgets('issue 3 (feedback-pruebas): the results list is topped by the ' + 'standard section gap, not the horizontal-inset constant reused for ' + 'a vertical axis', (tester) async { + _setLargeSurfaceSize(tester); + final estado = _crearEstado( + radio: FakeServicioRadio( + busqueda: [emisoraDemo(uuid: 'r-1', nombre: 'Radio Uno')], + ), + ); + addTearDown(estado.dispose); + await tester.runAsync(estado.inicializar); + + await tester.pumpWidget(_conProviders(estado, _testApp())); + await _pumpStableFrame(tester); + await tester.enterText(find.byType(SearchBar), 'radio'); + await tester.testTextInput.receiveAction(TextInputAction.done); + await _pumpStableFrame(tester); + + // Reads the structural padding directly, rather than measuring a + // gap between two rendered widgets — the count row's own height is + // dictated by its taller PopupMenuButton (48dp touch target), so a + // position-based gap measurement against the count TEXT specifically + // would be thrown off by that unrelated vertical centring. + // + // Scoped to `shrinkWrap: true` — the OUTER page ListView is ALSO an + // ancestor of every `FilaEmisoraPlana`, but only `_resultados`'s OWN + // inner `ListView.builder` sets `shrinkWrap`. + final listaResultados = tester.widget( + find.byWidgetPredicate((w) => w is ListView && w.shrinkWrap), + ); + final padding = listaResultados.padding as EdgeInsets; + + expect( + padding.top, + PluriLayout.sectionGap, + reason: + 'issue 3: the results list must use the dedicated vertical ' + 'section gap above its first row, not the horizontal (16) ' + 'constant reused for a vertical axis', + ); + }); + testWidgets( 'tapping the favourite toggle on a search result adds it to favorites', (tester) async {