fix(ajustes): stop settings row titles from wrapping and cutting off

FilaAjuste's title Text had no maxLines/overflow, and neither did its
trailing current-value Text. An unbounded value (e.g. a real station
name in "Emisora preferida") let the trailing Row claim unbounded
width, squeezing the title down until it wrapped across several lines
that the row's fixed height then cut short.

Constrain the title to a single ellipsized line and cap the trailing
value's width the same way. FilaAjuste backs all 12 settings rows, so
every row is protected, not just the one that happened to expose it.
This commit is contained in:
2026-07-30 19:08:06 +02:00
parent c7e1a212ca
commit e75f010b98
2 changed files with 99 additions and 7 deletions
+30 -7
View File
@@ -93,6 +93,14 @@ class FilaAjuste extends StatelessWidget {
/// "no accent" — the icon renders exactly as before.
final Color? iconColor;
/// Issue 5 (feedback-pruebas): caps how much width the trailing current
/// value can claim. `ListTile` gives `trailing` as much width as it wants
/// before handing the title whatever is left — an unbounded value (e.g. a
/// real, arbitrarily long station name for "Emisora preferida") could
/// squeeze the title down to almost nothing, forcing it to wrap across
/// several lines that then get cut short by the row's fixed height.
static const _anchoMaximoValor = 108.0;
@override
Widget build(BuildContext context) {
final type = context.pluriType;
@@ -106,17 +114,32 @@ class FilaAjuste extends StatelessWidget {
// 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)),
// Issue 5: constrained to one line, ellipsizing instead of wrapping —
// labels must wrap as little as possible and never render visibly
// truncated (a multi-line wrap inside this fixed-height row cuts the
// last line short, which reads as broken, not as intentional).
title: Text(
titulo,
style: type.cardTitle.copyWith(fontSize: 14),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
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),
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: _anchoMaximoValor),
child: 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),
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.end,
),
),
const SizedBox(width: 6),
@@ -98,6 +98,75 @@ void main() {
},
);
testWidgets(
'issue 5 (feedback-pruebas): a long trailing value does not squeeze the '
'title into wrapping across multiple lines -- the title stays on ONE '
'line, ellipsizing instead',
(tester) async {
const titulo = 'Emisora preferida';
const valorLargo = 'Radio Nacional Clasica Internacional FM Stereo HD';
await tester.pumpWidget(
host(
SizedBox(
width: 360,
child: FilaAjuste(
icon: Icons.radio_rounded,
titulo: titulo,
valor: valorLargo,
onTap: () {},
),
),
),
);
await tester.pump();
expect(
tester.takeException(),
isNull,
reason: 'a squeezed row must not overflow either',
);
final tituloWidget = tester.widget<Text>(find.text(titulo));
expect(
tituloWidget.maxLines,
1,
reason: 'issue 5: the title must be constrained to a single line',
);
expect(tituloWidget.overflow, TextOverflow.ellipsis);
// Measured, not just `find.text` (a wrapped-but-still-present Text
// would still satisfy a bare `find.text` match, per the known
// "find.text can't catch visual wrap" trap) -- compare the rendered
// height against the SAME style/width rendered with a title that is
// guaranteed to fit on one line.
final alturaConValorLargo = tester.getSize(find.text(titulo)).height;
await tester.pumpWidget(
host(
SizedBox(
width: 360,
child: FilaAjuste(
icon: Icons.radio_rounded,
titulo: titulo,
onTap: () {},
),
),
),
);
await tester.pump();
final alturaReferencia = tester.getSize(find.text(titulo)).height;
expect(
alturaConValorLargo,
closeTo(alturaReferencia, 1.0),
reason:
'the title rendered taller with a long value present -- it '
'wrapped instead of staying on a single ellipsized line',
);
},
);
testWidgets(
'visual fidelity (audit S10): GrupoAjustes insets its row divider by '
'47px, not full-bleed (t4 line 516)',