fix(inicio): render Tus emisoras as a 2-column grid of 8
Audit 1.10 (t4 lines 82-88): the prototype shows a 2x2+ grid of 8 favorite stations; the build was a 260px-wide horizontal strip capped at 6. Raises the cap to 8 and replaces the horizontal ListView with a GridView.builder (2 columns, gap 10). Adds a dedicated _CeldaTusEmisoras cell (44px square art radius 11, name + genre) rather than reusing TarjetaEmisora(esCompacta: true), since that widget always renders a favorite button and a live badge that this prototype cell never draws.
This commit is contained in:
@@ -17,7 +17,6 @@ import '../widgets/pluri_premium_widgets.dart';
|
||||
import '../widgets/pluri_root_header.dart';
|
||||
import '../widgets/pluri_sleep_timer_sheet.dart';
|
||||
import '../widgets/visualizador_audio.dart';
|
||||
import 'package:pluriwave/widgets/tarjeta_emisora.dart';
|
||||
|
||||
import 'pantalla_reproductor.dart';
|
||||
import 'reproducir_minimizado.dart';
|
||||
@@ -78,7 +77,10 @@ class _PantallaInicioState extends State<PantallaInicio> {
|
||||
/// todas" switching the root tab via `EstadoNavegacionRaiz.irA` rather
|
||||
/// than pushing a route (`app-navigation-shell` — Root-to-Root Switching
|
||||
/// Without Push).
|
||||
static const _capTusEmisoras = 6;
|
||||
///
|
||||
/// Audit 1.10 (t4 lines 82-88): the prototype shows a 2-column grid of 8,
|
||||
/// not a 6-capped horizontal strip.
|
||||
static const _capTusEmisoras = 8;
|
||||
|
||||
Widget _seccionTusEmisoras(
|
||||
BuildContext context,
|
||||
@@ -130,24 +132,26 @@ class _PantallaInicioState extends State<PantallaInicio> {
|
||||
),
|
||||
)
|
||||
else
|
||||
SizedBox(
|
||||
height: 76,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: mostrados.length,
|
||||
separatorBuilder: (_, __) => const SizedBox(width: 8),
|
||||
itemBuilder: (context, i) {
|
||||
final emisora = mostrados[i];
|
||||
return SizedBox(
|
||||
width: 260,
|
||||
child: TarjetaEmisora(
|
||||
emisora: emisora,
|
||||
esCompacta: true,
|
||||
onTap: () => reproducirMinimizado(context, emisora),
|
||||
),
|
||||
);
|
||||
},
|
||||
// Audit 1.10 (t4 lines 82-88): a 2-column grid, gap 10 — was a
|
||||
// horizontal strip of 260px-wide compact rows.
|
||||
GridView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: mostrados.length,
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 10,
|
||||
crossAxisSpacing: 10,
|
||||
childAspectRatio: 2.6,
|
||||
),
|
||||
itemBuilder: (context, i) {
|
||||
final emisora = mostrados[i];
|
||||
return _CeldaTusEmisoras(
|
||||
emisora: emisora,
|
||||
onTap: () => reproducirMinimizado(context, emisora),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -569,3 +573,114 @@ String? _metaEscuchar(Emisora emisora) {
|
||||
];
|
||||
return partes.isEmpty ? null : partes.join(' · ');
|
||||
}
|
||||
|
||||
/// Audit 1.10 (t4 lines 82-88): the "Tus emisoras" grid cell — a 44px
|
||||
/// square thumbnail (radius 11), name (13/w700/lh1.2) and genre
|
||||
/// (11/55%). Deliberately NOT `TarjetaEmisora(esCompacta: true)`: that
|
||||
/// widget always renders a favorite button and a live badge, neither of
|
||||
/// which the prototype's grid cell draws (t4 line 84-87 is art + two text
|
||||
/// lines, nothing else).
|
||||
class _CeldaTusEmisoras extends StatelessWidget {
|
||||
const _CeldaTusEmisoras({required this.emisora, required this.onTap});
|
||||
|
||||
final Emisora emisora;
|
||||
final VoidCallback onTap;
|
||||
|
||||
static const _ladoArte = 44.0;
|
||||
static const _radioArte = 11.0;
|
||||
static const _radioCelda = 16.0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final theme = Theme.of(context);
|
||||
final stationName = localizedStationName(l10n, emisora.nombre);
|
||||
final genero = emisora.generos.isNotEmpty ? emisora.generos.first : null;
|
||||
|
||||
return Semantics(
|
||||
button: true,
|
||||
label: l10n.stationSemanticLabel(stationName),
|
||||
child: PluriGlassSurface(
|
||||
borderRadius: BorderRadius.circular(_radioCelda),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(_radioCelda),
|
||||
onTap: onTap,
|
||||
child: Row(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(_radioArte),
|
||||
child: SizedBox(
|
||||
width: _ladoArte,
|
||||
height: _ladoArte,
|
||||
child: _arte(theme),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
stationName,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w700,
|
||||
height: 1.2,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (genero != null)
|
||||
Text(
|
||||
genero,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: theme.colorScheme.onSurface.withValues(
|
||||
alpha: 0.55,
|
||||
),
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _arte(ThemeData theme) {
|
||||
if (emisora.favicon != null && emisora.favicon!.isNotEmpty) {
|
||||
return CachedNetworkImage(
|
||||
imageUrl: emisora.favicon!,
|
||||
fit: BoxFit.cover,
|
||||
placeholder: (_, __) => _shimmer(theme),
|
||||
errorWidget: (_, __, ___) => _iconoFallback(theme),
|
||||
);
|
||||
}
|
||||
return _iconoFallback(theme);
|
||||
}
|
||||
|
||||
Widget _shimmer(ThemeData theme) => shimmer.Shimmer.fromColors(
|
||||
baseColor: theme.colorScheme.surfaceContainerHighest,
|
||||
highlightColor: theme.colorScheme.surface,
|
||||
child: Container(color: theme.colorScheme.surfaceContainerHighest),
|
||||
);
|
||||
|
||||
Widget _iconoFallback(ThemeData theme) => Container(
|
||||
color: theme.colorScheme.primaryContainer,
|
||||
child: Icon(
|
||||
Icons.radio_rounded,
|
||||
size: 20,
|
||||
color: theme.colorScheme.onPrimaryContainer,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user