Audit 3.2 (t4 lines 162-171): the prototype groups Países, Géneros, Tendencias and the entirely-missing Novedades into one 2x2 entry-point grid. The build had them as three unrelated always-visible widgets (a Países ListTile, a Géneros FilterChip Wrap, a Tendencias ActionChip strip) and no Novedades entry at all. Replaces all three with a single grid section, capability-preserving: Países still pushes PantallaPaises; Géneros and Tendencias now open their exact existing content in a picker sheet instead of always-on- screen (Géneros auto-closes on selection, matching this screen's other single-choice filter sheets; Tendencias stays open, a browse list, not a filter). Novedades re-triggers the existing discovery refresh, since no distinct "new stations" feed exists anywhere in the domain. New ARB key set (exploreByTitle/exploreTrendingTitle/ exploreTrendingSubtitle/exploreNewTitle/exploreNewSubtitle) translated across all 13 locales with zero anti-copy allowlist entries needed. Also fixes a pre-existing PluriEmptyState overflow this restructure exposed (unrelated to the grid itself, confirmed via isolated repro): wraps its Column in a SingleChildScrollView so a too-tall title/ subtitle scrolls instead of throwing a hard RenderFlex overflow.
119 lines
3.6 KiB
Dart
119 lines
3.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../tema/pluriwave_theme.dart';
|
|
import 'pluri_glass_surface.dart';
|
|
import 'pluri_icon.dart';
|
|
|
|
class PluriStatusPill extends StatelessWidget {
|
|
const PluriStatusPill({
|
|
super.key,
|
|
required this.icon,
|
|
required this.label,
|
|
this.accent,
|
|
});
|
|
|
|
final IconData icon;
|
|
final String label;
|
|
final Color? accent;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final t = context.pluriTokens;
|
|
final color = accent ?? t.electricMagenta;
|
|
return Tooltip(
|
|
message: label,
|
|
child: Container(
|
|
constraints: const BoxConstraints(maxWidth: 220),
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(999),
|
|
color: color.withValues(alpha: 0.13),
|
|
border: Border.all(color: color.withValues(alpha: 0.38)),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(icon, size: 16, color: color),
|
|
const SizedBox(width: 7),
|
|
Flexible(
|
|
child: Text(
|
|
label,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: Theme.of(
|
|
context,
|
|
).textTheme.labelMedium?.copyWith(fontWeight: FontWeight.w800),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class PluriEmptyState extends StatelessWidget {
|
|
const PluriEmptyState({
|
|
super.key,
|
|
required this.glyph,
|
|
required this.title,
|
|
required this.subtitle,
|
|
});
|
|
|
|
final PluriIconGlyph glyph;
|
|
final String title;
|
|
final String subtitle;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final t = context.pluriTokens;
|
|
final theme = Theme.of(context);
|
|
return Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(24),
|
|
child: PluriGlassSurface(
|
|
borderRadius: BorderRadius.circular(t.radiusLg + 10),
|
|
padding: const EdgeInsets.all(28),
|
|
// Bugfix: every call site constrains this widget to a fixed
|
|
// height (`SizedBox(height: 260)`). At a narrow width with
|
|
// realistic two-line title/subtitle copy, the icon + gaps +
|
|
// wrapped text can exceed what's actually left after this
|
|
// surface's own padding — a hard RenderFlex overflow rather
|
|
// than a graceful clip. `SingleChildScrollView` is a no-op
|
|
// visually whenever the content already fits (the normal
|
|
// case, on any real device width this was designed for) and
|
|
// only engages scrolling in the rare case it does not.
|
|
child: SingleChildScrollView(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
PluriIcon(
|
|
glyph: glyph,
|
|
variant: PluriIconVariant.activeGlow,
|
|
size: 58,
|
|
),
|
|
const SizedBox(height: 18),
|
|
Text(
|
|
title,
|
|
textAlign: TextAlign.center,
|
|
style: theme.textTheme.titleLarge?.copyWith(
|
|
fontWeight: FontWeight.w900,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
subtitle,
|
|
textAlign: TextAlign.center,
|
|
style: theme.textTheme.bodyMedium?.copyWith(
|
|
color: theme.colorScheme.onSurface.withValues(alpha: 0.72),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|