fix(favoritos,grabaciones): replace glass cards with flat rows
Item 23 / audit 4.3, 12.4 (t4:226-232, 616-619): Favoritos and Grabaciones rows were full glass cards / ListTiles with two stacked buttons and no artwork slot. Replace with flat, background-less rows via a new shared FilaEmisoraPlana widget (square art, name+meta, a circular play affordance) plus a bespoke Grabaciones row (44x12 placeholder art -- recordings carry no per-station favicon, so this is a themed fallback, not invented artwork). Favoritos keeps "Move to list" / "Remove from favorites" behind an overflow menu (same underlying methods, unchanged) instead of two always-visible buttons, since dropping either would be a functional regression the prototype's own row doesn't have to solve for. Also 12.1 (t4:610): the Grabaciones header action is folder_open, not a generic gear.
This commit is contained in:
@@ -8,6 +8,7 @@ import 'package:share_plus/share_plus.dart' show Share, XFile;
|
||||
import '../estado/estado_grabacion.dart';
|
||||
import '../l10n/gen/app_localizations.dart';
|
||||
import '../modelos/archivo_grabacion.dart';
|
||||
import '../tema/pluriwave_tokens.dart';
|
||||
import '../widgets/pluri_glass_surface.dart';
|
||||
import '../widgets/pluri_icon.dart';
|
||||
import '../widgets/pluri_layout.dart';
|
||||
@@ -231,7 +232,9 @@ class _PantallaGrabacionesState extends State<PantallaGrabaciones> {
|
||||
title: l10n.recordingsLibraryTitle,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.settings_outlined),
|
||||
// Audit 12.1 (t4:610): the header action is `folder_open` at
|
||||
// 22px, not a generic gear.
|
||||
icon: const Icon(Icons.folder_open_rounded, size: 22),
|
||||
tooltip: l10n.recordingsLibrarySettingsTooltip,
|
||||
onPressed:
|
||||
() => PluriPushScaffold.push(
|
||||
@@ -257,20 +260,27 @@ class _PantallaGrabacionesState extends State<PantallaGrabaciones> {
|
||||
subtitle: l10n.recordingsLibraryEmptySubtitle,
|
||||
)
|
||||
else
|
||||
for (final archivo in archivos)
|
||||
_FilaGrabacion(
|
||||
archivo: archivo,
|
||||
reproduciendo:
|
||||
_reproductor.rutaActual == archivo.ruta &&
|
||||
_reproductor.reproduciendo,
|
||||
duracion: _duracionPara(archivo.ruta),
|
||||
formatearDuracion: _formatearDuracion,
|
||||
formatearFecha: _formatearFecha,
|
||||
formatearBytes: _formatearBytes,
|
||||
onAlternarReproduccion:
|
||||
() => _alternarReproduccion(archivo.ruta),
|
||||
onAccionMenu: (accion) => _manejarAccion(accion, archivo),
|
||||
),
|
||||
// t4:618: `gap:2px` between rows.
|
||||
PluriPanelColumn(
|
||||
gap: 2,
|
||||
children: [
|
||||
for (final archivo in archivos)
|
||||
_FilaGrabacion(
|
||||
archivo: archivo,
|
||||
reproduciendo:
|
||||
_reproductor.rutaActual == archivo.ruta &&
|
||||
_reproductor.reproduciendo,
|
||||
duracion: _duracionPara(archivo.ruta),
|
||||
formatearDuracion: _formatearDuracion,
|
||||
formatearFecha: _formatearFecha,
|
||||
formatearBytes: _formatearBytes,
|
||||
onAlternarReproduccion:
|
||||
() => _alternarReproduccion(archivo.ruta),
|
||||
onAccionMenu:
|
||||
(accion) => _manejarAccion(accion, archivo),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -337,48 +347,116 @@ class _FilaGrabacion extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return PluriGlassSurface(
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: IconButton(
|
||||
icon: Icon(
|
||||
reproduciendo
|
||||
? Icons.pause_circle_filled_rounded
|
||||
: Icons.play_circle_fill_rounded,
|
||||
// Item 23 / audit 12.4 (t4:616-619): a flat, background-less row --
|
||||
// 44x44/radius-12 art placeholder (recordings carry no per-station
|
||||
// favicon, so this is a themed fallback square, not invented artwork),
|
||||
// name, meta line, a 24px play/pause affordance, and the SAME "-"
|
||||
// menu (Rename/Share/Delete) as before, just restyled.
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
ClipRRect(
|
||||
key: const ValueKey('fila-grabacion-arte'),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
width: 44,
|
||||
height: 44,
|
||||
color: theme.colorScheme.primaryContainer,
|
||||
child: Icon(
|
||||
Icons.radio_rounded,
|
||||
size: 22,
|
||||
color: theme.colorScheme.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: onAlternarReproduccion,
|
||||
),
|
||||
title: Text(archivo.nombre),
|
||||
subtitle: FutureBuilder<Duration?>(
|
||||
future: duracion,
|
||||
builder: (context, snap) {
|
||||
return Text(
|
||||
'${formatearFecha(archivo.fecha)} · '
|
||||
'${formatearDuracion(snap.data)} · '
|
||||
'${formatearBytes(archivo.tamanoBytes)}',
|
||||
);
|
||||
},
|
||||
),
|
||||
trailing: PopupMenuButton<String>(
|
||||
icon: const Icon(Icons.more_vert_rounded),
|
||||
onSelected: onAccionMenu,
|
||||
itemBuilder:
|
||||
(context) => [
|
||||
PopupMenuItem(
|
||||
value: 'rename',
|
||||
child: Text(l10n.recordingActionRename),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// t4:619: 14.5px/w700.
|
||||
Text(
|
||||
archivo.nombre,
|
||||
style: const TextStyle(
|
||||
fontSize: 14.5,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'share',
|
||||
child: Text(l10n.recordingActionShare),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'delete',
|
||||
child: Text(l10n.recordingActionDelete),
|
||||
// t4:619: 12px/rgba(242,247,250,.55).
|
||||
FutureBuilder<Duration?>(
|
||||
future: duracion,
|
||||
builder: (context, snap) {
|
||||
return Text(
|
||||
'${formatearFecha(archivo.fecha)} · '
|
||||
'${formatearDuracion(snap.data)} · '
|
||||
'${formatearBytes(archivo.tamanoBytes)}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.onSurface.withValues(
|
||||
alpha: 0.55,
|
||||
),
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// t4:619: play_circle 24px brand teal in a 42x42 target.
|
||||
SizedBox(
|
||||
width: 42,
|
||||
height: 42,
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
tooltip: reproduciendo ? l10n.pauseAction : l10n.playAction,
|
||||
icon: Icon(
|
||||
reproduciendo
|
||||
? Icons.pause_circle_filled_rounded
|
||||
: Icons.play_circle_fill_rounded,
|
||||
size: 24,
|
||||
color: PluriWaveTokens.brand,
|
||||
),
|
||||
onPressed: onAlternarReproduccion,
|
||||
),
|
||||
),
|
||||
// t4:619: more_vert 20px/45% in a 38x42 target.
|
||||
SizedBox(
|
||||
width: 38,
|
||||
height: 42,
|
||||
child: PopupMenuButton<String>(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: Icon(
|
||||
Icons.more_vert_rounded,
|
||||
size: 20,
|
||||
color: theme.colorScheme.onSurface.withValues(alpha: 0.45),
|
||||
),
|
||||
onSelected: onAccionMenu,
|
||||
itemBuilder:
|
||||
(context) => [
|
||||
PopupMenuItem(
|
||||
value: 'rename',
|
||||
child: Text(l10n.recordingActionRename),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'share',
|
||||
child: Text(l10n.recordingActionShare),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'delete',
|
||||
child: Text(l10n.recordingActionDelete),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user