diff --git a/lib/pantallas/ajustes/widgets/fila_ajuste.dart b/lib/pantallas/ajustes/widgets/fila_ajuste.dart index 6e87f05..75f52bf 100644 --- a/lib/pantallas/ajustes/widgets/fila_ajuste.dart +++ b/lib/pantallas/ajustes/widgets/fila_ajuste.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../../tema/pluriwave_theme.dart'; import '../../../widgets/pluri_glass_surface.dart'; +import '../../../widgets/pluri_layout.dart'; /// Design ADR-3: the two nav-row primitives every Settings detail screen is /// reached through. [GrupoAjustes] is a single [PluriGlassSurface] card @@ -27,20 +28,36 @@ class GrupoAjustes extends StatelessWidget { @override Widget build(BuildContext context) { final type = context.pluriType; - return PluriGlassSurface( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(titulo, style: type.eyebrowLabel), - const SizedBox(height: 4), - for (var i = 0; i < filas.length; i++) ...[ - // S10 (Tier 4 visual fidelity): the prototype insets its row - // divider by 47px (t4 line 516), not full-bleed. - if (i > 0) const Divider(height: 1, indent: 47), - filas[i], - ], - ], - ), + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Audit 10.2 (t4 line 511): the group eyebrow sits OUTSIDE the + // card entirely, at title-tier (20px) padding -- it used to live + // INSIDE the PluriGlassSurface, sharing the card's own 16px + // padding. + Padding( + padding: const EdgeInsets.fromLTRB( + PluriLayout.titleHorizontal, + 0, + PluriLayout.titleHorizontal, + 6, + ), + child: Text(titulo, style: type.eyebrowLabel), + ), + PluriGlassSurface( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (var i = 0; i < filas.length; i++) ...[ + // S10 (Tier 4 visual fidelity): the prototype insets its + // row divider by 47px (t4 line 516), not full-bleed. + if (i > 0) const Divider(height: 1, indent: 47), + filas[i], + ], + ], + ), + ), + ], ); } } @@ -56,6 +73,7 @@ class FilaAjuste extends StatelessWidget { required this.titulo, required this.onTap, this.valor, + this.iconColor, }); final IconData icon; @@ -68,6 +86,13 @@ class FilaAjuste extends StatelessWidget { /// "no current value to show" — the row renders exactly as before. final String? valor; + /// Audit 10.5 (t4 lines 514/516/522 — equalizer `#21D4D9`, hd + /// `#7EE4C2`, folder `#F4B860`): only the first row or two of a group + /// carries an accent colour in the prototype; every other row's icon + /// stays the ambient default. Null (the vast majority of rows) means + /// "no accent" — the icon renders exactly as before. + final Color? iconColor; + @override Widget build(BuildContext context) { final type = context.pluriType; @@ -76,7 +101,7 @@ class FilaAjuste extends StatelessWidget { contentPadding: EdgeInsets.zero, // 10.6 (Tier 4 visual fidelity): the prototype's row icon is 21px (t4 // line 514), not Material's 24px default. - leading: Icon(icon, size: 21), + leading: Icon(icon, size: 21, color: iconColor), // 10.8 (Tier 4 visual fidelity): the prototype's row title is // 14px/w700 (t4 line 514); cardTitle is 14.5/w700 — a one-off // override, not a new PluriWaveTypography style (mirrors the diff --git a/lib/pantallas/pantalla_ajustes.dart b/lib/pantallas/pantalla_ajustes.dart index 4d14c0c..8ff6e3d 100644 --- a/lib/pantallas/pantalla_ajustes.dart +++ b/lib/pantallas/pantalla_ajustes.dart @@ -9,6 +9,7 @@ import '../l10n/display_names.dart'; import '../l10n/gen/app_localizations.dart'; import '../modelos/archivo_grabacion.dart'; import '../modelos/emisora.dart'; +import '../tema/pluriwave_tokens.dart'; import '../widgets/pluri_layout.dart'; import '../widgets/pluri_push_scaffold.dart'; import '../widgets/pluri_root_header.dart'; @@ -106,6 +107,9 @@ class _AjustesContent extends StatelessWidget { filas: [ FilaAjuste( icon: Icons.equalizer_rounded, + // Audit 10.5 (t4 line 514): the first AUDIO row's icon is + // brand cyan. + iconColor: PluriWaveTokens.brand, titulo: l10n.equalizerTitle, valor: ecualizadorActivo @@ -149,6 +153,9 @@ class _AjustesContent extends StatelessWidget { filas: [ FilaAjuste( icon: Icons.playlist_add_check_circle_rounded, + // Audit 10.5 (t4 line 526): the first STATIONS row's icon + // is warmCoral (the prototype's own `folder` row accent). + iconColor: PluriWaveTokens.dark.warmCoral, titulo: l10n.favoriteGroupsTitle, valor: '$gruposCount', onTap: diff --git a/test/pantallas/ajustes/widgets/fila_ajuste_test.dart b/test/pantallas/ajustes/widgets/fila_ajuste_test.dart index ccc31cd..61a34b8 100644 --- a/test/pantallas/ajustes/widgets/fila_ajuste_test.dart +++ b/test/pantallas/ajustes/widgets/fila_ajuste_test.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pluriwave/pantallas/ajustes/widgets/fila_ajuste.dart'; import 'package:pluriwave/tema/pluriwave_theme.dart'; +import 'package:pluriwave/widgets/pluri_glass_surface.dart'; /// S8 (Tier 1 visual fidelity): the prototype puts a trailing "current /// value" on nearly every settings row, 13px `rgba(242,247,250,.55)` (t4 @@ -122,4 +123,81 @@ void main() { expect(divider.indent, 47); }, ); + + testWidgets('visual fidelity (audit 10.5): an explicit iconColor tints the ' + 'leading icon (t4 lines 514/516/522 -- equalizer/hd/folder accents)', ( + tester, + ) async { + await tester.pumpWidget( + host( + FilaAjuste( + icon: Icons.equalizer_rounded, + titulo: 'Ecualizador base', + iconColor: const Color(0xFF21D4D9), + onTap: () {}, + ), + ), + ); + + final icon = tester.widget(find.byIcon(Icons.equalizer_rounded)); + expect(icon.color, const Color(0xFF21D4D9)); + }); + + testWidgets( + 'visual fidelity (audit 10.5): omitting iconColor keeps the default ' + '(unchanged pre-10.5 behaviour)', + (tester) async { + await tester.pumpWidget( + host( + FilaAjuste( + icon: Icons.language_rounded, + titulo: 'Idioma', + onTap: () {}, + ), + ), + ); + + final icon = tester.widget(find.byIcon(Icons.language_rounded)); + expect(icon.color, isNull); + }, + ); + + testWidgets( + 'visual fidelity (audit 10.2): the group eyebrow sits OUTSIDE the ' + 'card, at title-tier (20px) padding -- not inside the ' + 'PluriGlassSurface (t4 line 511)', + (tester) async { + _suppressListTileInkAssertion(); + await tester.pumpWidget( + host( + GrupoAjustes( + titulo: 'AUDIO', + filas: [ + FilaAjuste( + icon: Icons.equalizer_rounded, + titulo: 'Uno', + onTap: () {}, + ), + ], + ), + ), + ); + + expect( + find.ancestor( + of: find.text('AUDIO'), + matching: find.byType(PluriGlassSurface), + ), + findsNothing, + reason: 'the eyebrow must not be a descendant of the card', + ); + + final eyebrowLeft = tester.getTopLeft(find.text('AUDIO')).dx; + expect( + eyebrowLeft, + 20, + reason: 'S5 title tier -- PluriLayout.titleHorizontal', + ); + }, + ); }