Tier 4 visual fidelity, audit S10/10.6/10.8/10.9 (t4 lines 514-516): GrupoAjustes' row divider now indents 47px instead of running full-bleed, FilaAjuste's leading icon drops from Material's 24px default to 21px, the row title is a local 14px override of cardTitle (14.5), and the chevron shrinks to 19px at 40% opacity instead of the 24px full-opacity default.
113 lines
4.3 KiB
Dart
113 lines
4.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../../../tema/pluriwave_theme.dart';
|
|
import '../../../widgets/pluri_glass_surface.dart';
|
|
|
|
/// Design ADR-3: the two nav-row primitives every Settings detail screen is
|
|
/// reached through. [GrupoAjustes] is a single [PluriGlassSurface] card
|
|
/// carrying a [PluriWaveTypography.eyebrowLabel] group heading and a list of
|
|
/// [FilaAjuste] rows, each pushing its detail screen via
|
|
/// `PluriPushScaffold.push`. Neither primitive owns any business logic or
|
|
/// provider read — they are pure navigation chrome, which is what keeps the
|
|
/// Settings root down to "grouped nav rows only".
|
|
class GrupoAjustes extends StatelessWidget {
|
|
const GrupoAjustes({super.key, required this.titulo, required this.filas});
|
|
|
|
/// Group heading, styled with [PluriWaveTypography.eyebrowLabel]. Authored
|
|
/// already in its display form — this style never applies `toUpperCase()`.
|
|
final String titulo;
|
|
|
|
/// S8 (Tier 1 visual fidelity): `Widget`, not `List<FilaAjuste>` — a few
|
|
/// rows source their current-value text asynchronously (e.g. recordings
|
|
/// count, app version) and wrap their own `FilaAjuste` in a
|
|
/// `FutureBuilder`. `GrupoAjustes` only iterates and inserts dividers; it
|
|
/// never reaches into `FilaAjuste`-specific state.
|
|
final List<Widget> filas;
|
|
|
|
@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],
|
|
],
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// A single Settings navigation row: icon, title, an optional trailing
|
|
/// current-value string, and a trailing chevron. Tapping it is the row's
|
|
/// only behaviour — it carries no switches, sliders or text fields, which
|
|
/// is what "zero inline controls" means at the root.
|
|
class FilaAjuste extends StatelessWidget {
|
|
const FilaAjuste({
|
|
super.key,
|
|
required this.icon,
|
|
required this.titulo,
|
|
required this.onTap,
|
|
this.valor,
|
|
});
|
|
|
|
final IconData icon;
|
|
final String titulo;
|
|
final VoidCallback onTap;
|
|
|
|
/// S8 (Tier 1 visual fidelity): the prototype puts a trailing current
|
|
/// value on nearly every row (t4 lines 512-539, 625 — e.g. "3 guardados",
|
|
/// "Alfabético", "Español"), 13px `rgba(242,247,250,.55)`. Null means
|
|
/// "no current value to show" — the row renders exactly as before.
|
|
final String? valor;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final type = context.pluriType;
|
|
final valorActual = valor;
|
|
return ListTile(
|
|
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),
|
|
// 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
|
|
// precedent set for the ringing screen's station name, audit 9.7).
|
|
title: Text(titulo, style: type.cardTitle.copyWith(fontSize: 14)),
|
|
trailing: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
if (valorActual != null) ...[
|
|
Text(
|
|
valorActual,
|
|
// bodyStrong is already 13/w600, matching the prototype's row
|
|
// value spec exactly — only the colour needs overriding.
|
|
style: type.bodyStrong.copyWith(
|
|
color: const Color(0xFFF2F7FA).withValues(alpha: 0.55),
|
|
),
|
|
),
|
|
const SizedBox(width: 6),
|
|
],
|
|
// 10.9 (Tier 4 visual fidelity): the prototype's chevron is 19px
|
|
// at 40% opacity (t4 lines 515-539), not Material's 24px
|
|
// full-opacity default.
|
|
Icon(
|
|
Icons.chevron_right_rounded,
|
|
size: 19,
|
|
color: const Color(0xFFF2F7FA).withValues(alpha: 0.4),
|
|
),
|
|
],
|
|
),
|
|
onTap: onTap,
|
|
);
|
|
}
|
|
}
|