fix(ajustes): group eyebrow outside the card, per-row accent icons
Audit 10.2 (t4:511): GrupoAjustes's eyebrow now sits OUTSIDE the PluriGlassSurface, at title-tier (20px) padding -- it used to share the card's own 16px content padding. Audit 10.5 (t4:514/526): FilaAjuste gains an optional iconColor. Wired on the two rows the prototype actually accents: AUDIO's Ecualizador (brand cyan) and STATIONS' Grupos de favoritos (warmCoral). Item 10.10 (two extra AUDIO rows -- "Calidad de streaming" and "Reproduccion sin interrupciones") is NOT implemented: neither concept has any backing state or service anywhere in this app today. Building them for real means inventing two new persisted settings, and a genuine streaming-quality preference would plausibly need to touch the protected servicio_audio.dart to actually affect playback -- out of scope for a visual-fidelity pass. Decorative rows that open nothing would be dead UI, which is worse than leaving the gap disclosed.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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<Icon>(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<Icon>(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',
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user