fix(paises,buscar): tappable ISO rows and a reconnect card
Item 24 / audit 5.1-5.3, 5.5 (t4:255-269): Paises' "Tus idiomas" was a Wrap of non-interactive Chips, and the full list was a plain ListTile with no ISO column and no onTap. Both now share one tappable row (ISO code, name, station count, chevron); the first "Tus idiomas" row gets the prototype's teal-tinted highlight. The one production call site wires the tap to filter Buscar by that country's code and pop back -- EstadoBusqueda.buscar(pais: ...) already accepts any ISO alpha-2 code, not just the ~10 presets in the filter sheet. Item 25 / audit 13.2 (t4:643-646): a reconnect card (rotating ring, station name, "Reconectando...", a stop affordance) replaces the complete absence of any reconnect signal outside a word in the mini player. Ships WITHOUT the prototype's attempt counter: the only live ControladorReconexion instance is a private field of PluriWaveAudioHandler inside servicio_audio.dart, a file this task requires stay byte-identical to main, and nothing else re-exposes it. Reconstructing a count from estadoStream's reconectando emissions would not be faithful (the stream can emit it many times per actual backoff attempt), so that was deliberately not attempted.
This commit is contained in:
@@ -4,6 +4,7 @@ import 'package:provider/provider.dart';
|
||||
import '../estado/estado_busqueda.dart';
|
||||
import '../l10n/gen/app_localizations.dart';
|
||||
import '../modelos/pais_radio.dart';
|
||||
import '../tema/pluriwave_tokens.dart';
|
||||
import '../widgets/pluri_glass_surface.dart';
|
||||
import '../widgets/pluri_layout.dart';
|
||||
import '../widgets/pluri_push_scaffold.dart';
|
||||
@@ -15,7 +16,14 @@ import '../widgets/pluri_push_scaffold.dart';
|
||||
/// live station counts already parsed from the API's `stationcount`
|
||||
/// **string** field (`PaisRadio.fromApi`, Engram id 2500).
|
||||
class PantallaPaises extends StatefulWidget {
|
||||
const PantallaPaises({super.key});
|
||||
const PantallaPaises({super.key, this.onPaisSeleccionado});
|
||||
|
||||
/// Item 24 / audit 5.2 (t4:256-269): every row is tappable (the prototype
|
||||
/// draws a `chevron_right` on all of them). Optional so this screen stays
|
||||
/// usable stand-alone; the one production call site
|
||||
/// (`pantalla_buscar.dart`'s "Explorar por" grid) wires this to filter
|
||||
/// search results by the tapped country and pop back.
|
||||
final ValueChanged<PaisRadio>? onPaisSeleccionado;
|
||||
|
||||
@override
|
||||
State<PantallaPaises> createState() => _PantallaPaisesState();
|
||||
@@ -78,6 +86,10 @@ class _PantallaPaisesState extends State<PantallaPaises> {
|
||||
);
|
||||
}
|
||||
|
||||
void _seleccionar(PaisRadio pais) {
|
||||
widget.onPaisSeleccionado?.call(pais);
|
||||
}
|
||||
|
||||
Widget _seccionTusIdiomas(
|
||||
BuildContext context,
|
||||
List<PaisRadio> paises,
|
||||
@@ -105,18 +117,16 @@ class _PantallaPaisesState extends State<PantallaPaises> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
for (final pais in destacados)
|
||||
Chip(
|
||||
label: Text(
|
||||
'${pais.nombre} · ${l10n.stationsCount(pais.numeroEmisoras)}',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// Item 24 / audit 5.1 (t4:255-258): a column of tappable ISO
|
||||
// rows, not a Wrap of non-interactive Chips.
|
||||
for (final pais in destacados)
|
||||
_FilaPais(
|
||||
pais: pais,
|
||||
l10n: l10n,
|
||||
// Item 24 / audit 5.5 (t4:256): the first row is highlighted.
|
||||
destacado: pais == destacados.first,
|
||||
onTap: () => _seleccionar(pais),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -140,22 +150,100 @@ class _PantallaPaisesState extends State<PantallaPaises> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: paises.length,
|
||||
separatorBuilder: (_, __) => const Divider(height: 1),
|
||||
itemBuilder: (context, i) {
|
||||
final pais = paises[i];
|
||||
return ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(pais.nombre),
|
||||
trailing: Text(l10n.stationsCount(pais.numeroEmisoras)),
|
||||
);
|
||||
},
|
||||
),
|
||||
// Item 24 / audit 5.1-5.3 (t4:262-269): the same tappable ISO row
|
||||
// as "Tus idiomas" -- not the previous ListTile, which had no ISO
|
||||
// column and no onTap.
|
||||
for (final pais in paises)
|
||||
_FilaPais(pais: pais, l10n: l10n, onTap: () => _seleccionar(pais)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Item 24 / audit 5.1-5.3, 5.5 (t4:256-269): a tappable row -- ISO code
|
||||
/// column, name, station count, and a chevron -- shared by "Tus idiomas"
|
||||
/// and "Todos". [destacado] applies the t4:256 highlight (teal-tinted
|
||||
/// background, bold name) reserved for the very first "Tus idiomas" row.
|
||||
class _FilaPais extends StatelessWidget {
|
||||
const _FilaPais({
|
||||
required this.pais,
|
||||
required this.l10n,
|
||||
this.destacado = false,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
final PaisRadio pais;
|
||||
final AppLocalizations l10n;
|
||||
final bool destacado;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final onSurface = Theme.of(context).colorScheme.onSurface;
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
destacado
|
||||
? PluriWaveTokens.brand.withValues(alpha: 0.1)
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
// t4:256-258: 26px/lh1, centred in a 34-wide column.
|
||||
SizedBox(
|
||||
width: 34,
|
||||
child: Text(
|
||||
pais.codigoIso,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 26, height: 1),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
pais.nombre,
|
||||
style: TextStyle(
|
||||
fontSize: 15.5,
|
||||
fontWeight:
|
||||
destacado ? FontWeight.w800 : FontWeight.w700,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
l10n.stationsCount(pais.numeroEmisoras),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: onSurface.withValues(alpha: 0.55),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.chevron_right_rounded,
|
||||
size: 20,
|
||||
color: onSurface.withValues(alpha: 0.4),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user