aves_mio1/lib/remote/remote_image_tile.dart.old
FabioMich66 deb7b4c6dd
Some checks are pending
Quality check / Flutter analysis (push) Waiting to run
Quality check / CodeQL analysis (java-kotlin) (push) Waiting to run
super
2026-04-10 10:07:04 +02:00

189 lines
5.6 KiB
Dart

// lib/remote/remote_image_tile.dart
import 'package:flutter/material.dart';
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/props.dart'; // <-- necessario per entry.isVideo
import 'package:aves/remote/remote_http.dart';
/// Miniatura per contenuti **remoti** con overlay coerente a quello dei locali:
/// - Icona Play in basso-sinistra se è video
/// - Chip durata in basso-destra se `entry.durationMillis` è disponibile
///
/// Nota: per mostrare la durata è necessario che `entry.durationMillis` sia valorizzato nel DB.
/// Se non c'è (es. il server non la fornisce), mostreremo comunque il badge Play.
class RemoteImageTile extends StatelessWidget {
final AvesEntry entry;
final double borderRadius;
final BoxFit fit;
final Color? overlayIconBg; // per personalizzare il cerchio dietro l'icona
final Color? overlayIconFg;
final Color? durationBg;
final Color? durationFg;
const RemoteImageTile({
super.key,
required this.entry,
this.borderRadius = 12.0,
this.fit = BoxFit.cover, // Se vuoi evitare crop in grid usa BoxFit.contain
this.overlayIconBg,
this.overlayIconFg,
this.durationBg,
this.durationFg,
});
bool get _isRemote => entry.origin == 1;
@override
Widget build(BuildContext context) {
// URL assoluto per thumb o path remoto
final rel = entry.remoteThumb2 ?? entry.remoteThumb1 ?? entry.remotePath ?? entry.path;
if (!_isRemote || rel == null || rel.isEmpty) {
// Fallback: niente immagine -> box vuoto con bg tenue
return _frame(
context,
const ColoredBox(color: Colors.black12),
);
}
final url = RemoteHttp.absUrl(rel);
final ar = (entry.displayAspectRatio > 0) ? entry.displayAspectRatio : 1.0;
return AspectRatio(
aspectRatio: ar,
child: FutureBuilder<Map<String, String>>(
future: RemoteHttp.headers(),
builder: (context, snap) {
if (snap.connectionState != ConnectionState.done) {
return _frame(
context,
const ColoredBox(color: Colors.black12),
showProgress: true,
);
}
final hdrs = snap.data ?? const {};
final img = Image.network(
url,
fit: fit,
headers: hdrs.isEmpty ? null : hdrs,
errorBuilder: (_, __, ___) => const ColoredBox(color: Colors.black26),
);
return _frame(context, img);
},
),
);
}
Widget _frame(BuildContext context, Widget child, {bool showProgress = false}) {
final theme = Theme.of(context);
final radius = BorderRadius.circular(borderRadius);
final isVideo = entry.isVideo; // <-- disponibile grazie a props.dart
final showDuration = isVideo && (entry.durationMillis ?? 0) > 0;
return ClipRRect(
borderRadius: radius,
child: Stack(
fit: StackFit.expand,
children: [
Positioned.fill(child: child),
// Progress (placeholder) opzionale
if (showProgress)
const Center(
child: SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 1.5),
),
),
// === OVERLAY VIDEO ===
if (isVideo) ...[
// Badge Play (in basso a sinistra)
Positioned(
left: 6,
bottom: 6,
child: _PlayBadge(
bg: overlayIconBg ?? Colors.black.withOpacity(.55),
fg: overlayIconFg ?? Colors.white,
),
),
// Chip Durata (in basso a destra), se disponibile
if (showDuration)
Positioned(
right: 6,
bottom: 6,
child: _DurationChip(
text: entry.durationText,
bg: durationBg ?? Colors.black.withOpacity(.65),
fg: durationFg ?? Colors.white,
borderRadius: 10,
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
textStyle: theme.textTheme.labelSmall?.copyWith(
color: durationFg ?? Colors.white,
fontFeatures: const [FontFeature.tabularFigures()],
),
),
),
],
],
),
);
}
}
class _PlayBadge extends StatelessWidget {
final Color bg;
final Color fg;
const _PlayBadge({
required this.bg,
required this.fg,
});
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(color: bg, shape: BoxShape.circle),
child: Padding(
padding: const EdgeInsets.all(6),
child: Icon(Icons.play_arrow_rounded, color: fg, size: 16),
),
);
}
}
class _DurationChip extends StatelessWidget {
final String text;
final Color bg;
final Color fg;
final double borderRadius;
final EdgeInsets padding;
final TextStyle? textStyle;
const _DurationChip({
super.key,
required this.text,
required this.bg,
required this.fg,
this.borderRadius = 10,
this.padding = const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
this.textStyle,
});
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(
color: bg,
borderRadius: BorderRadius.circular(borderRadius),
),
child: Padding(
padding: padding,
child: Text(
text,
style: textStyle ?? Theme.of(context).textTheme.labelSmall?.copyWith(color: fg),
),
),
);
}
}