The prototype puts a trailing current-value string on nearly every settings row (t4 lines 512-539, 625 -- "3 guardados", "Alfabetico", "Espanol", "7 . 84 MB"). FilaAjuste only accepted icon/titulo/onTap, so every row was value-blind. Add an optional `valor` slot to FilaAjuste (13px, rgba(242,247,250,.55), rendered before the chevron). Wire 8 of the 12 built rows to state already available at the settings root: equalizer on/off, sleep-timer active, favourite-group count, preferred station name, custom-station count, sort order, recordings count-and-size (FutureBuilder over EstadoGrabacion.listarGrabaciones), and the current language (hoisted pantalla_ajustes_idioma.dart's native-name list to module level so the root can read it without duplicating it). Salida de audio, Musica local, Backup and Info's version are left without a value -- each lacks a low-risk, deterministically-testable data source (see the apply-progress note for the reason per row). Reading EstadoRadio for these values through a root `context.watch` would rebuild the whole settings list -- including the Grabaciones FutureBuilder's disk read -- on every unrelated audio notification; this follows the codebase's existing S4-R5 convention of narrow `context.select` per field instead. Ajustes' own PluriRootHeader/PluriScreenHeader edit (S2 in this same pass) landed in this commit too, since both touched the same header block in pantalla_ajustes.dart at the same time. S8, Tier 1 visual-fidelity pass (audit id 2521).
98 lines
3.4 KiB
Dart
98 lines
3.4 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++) ...[
|
|
if (i > 0) const Divider(height: 1),
|
|
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,
|
|
leading: Icon(icon),
|
|
title: Text(titulo, style: type.cardTitle),
|
|
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),
|
|
],
|
|
const Icon(Icons.chevron_right_rounded),
|
|
],
|
|
),
|
|
onTap: onTap,
|
|
);
|
|
}
|
|
}
|