aves_mio100/lib/widgets/collection/album_tile.dart
2026-07-06 14:52:45 +02:00

195 lines
5.4 KiB
Dart

import 'package:flutter/material.dart';
import 'album_controller.dart';
import 'album_actions.dart';
import 'package:aves/widgets/common/thumbnail/aves_thumbnail_widget.dart';
/// Tile principale usato nella griglia "Tutti gli album"
/// Basato su AlbumRow (derivato da FolderStats)
class AlbumTile extends StatelessWidget {
final AlbumRow album;
final VoidCallback onTap;
final VoidCallback onLongPress;
const AlbumTile({
super.key,
required this.album,
required this.onTap,
required this.onLongPress,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
onLongPress: onLongPress,
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Stack(
children: [
// COVER
Positioned.fill(
child: Hero(
tag: 'album:${album.path}',
child: AlbumTileThumbnail(album: album),
),
),
// GRADIENT OVERLAY
Positioned.fill(
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.center,
colors: [
Colors.black.withOpacity(0.7),
Colors.black.withOpacity(0.2),
Colors.transparent,
],
stops: const [0.0, 0.4, 1.0],
),
),
),
),
// BADGES (remoto, vault, hybrid, pinned, hidden)
Positioned(
top: 6,
right: 6,
child: Row(
children: [
if (album.isRemote)
const _Badge(icon: Icons.cloud_outlined),
if (album.isVault)
const _Badge(icon: Icons.lock_outline),
if (album.hybridType != 0)
const _Badge(icon: Icons.all_inclusive),
if (album.priority > 0)
const _Badge(icon: Icons.push_pin_outlined),
if (album.hidden)
const _Badge(icon: Icons.visibility_off_outlined),
],
),
),
// TESTO (nome + count + size)
Positioned(
left: 8,
right: 8,
bottom: 8,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
album.displayName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 15,
),
),
const SizedBox(height: 2),
Text(
'${album.fileCount} file',
style: const TextStyle(
color: Colors.white70,
fontSize: 12,
),
),
Text(
_formatSize(album.totalSize),
style: const TextStyle(
color: Colors.white60,
fontSize: 11,
),
),
],
),
),
// MENU RAPIDO (3 dots)
Positioned(
top: 0,
right: 0,
child: IconButton(
icon: const Icon(Icons.more_vert, color: Colors.white),
onPressed: () {
AlbumActions.showMenu(context, album);
},
),
),
],
),
),
);
}
String _formatSize(int bytes) {
if (bytes < 1024) return '$bytes B';
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
if (bytes < 1024 * 1024 * 1024) return '${(bytes / 1024 / 1024).toStringAsFixed(1)} MB';
return '${(bytes / 1024 / 1024 / 1024).toStringAsFixed(1)} GB';
}
}
/// Thumbnail con fallback
class AlbumTileThumbnail extends StatelessWidget {
final AlbumRow album;
final double radius;
const AlbumTileThumbnail({
super.key,
required this.album,
this.radius = 12,
});
@override
Widget build(BuildContext context) {
final entry = album.thumbEntry;
if (entry == null) {
return Container(
decoration: BoxDecoration(
color: Colors.grey.shade900,
borderRadius: BorderRadius.circular(radius),
),
child: const Icon(Icons.folder, size: 48, color: Colors.white54),
);
}
return ClipRRect(
borderRadius: BorderRadius.circular(radius),
child: AvesThumbnailWidget(
entry: entry,
heroTag: 'album:${album.path}',
fit: BoxFit.cover,
),
);
}
}
/// Badge icona piccolo
class _Badge extends StatelessWidget {
final IconData icon;
const _Badge({required this.icon});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(left: 4),
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.55),
borderRadius: BorderRadius.circular(6),
),
child: Icon(icon, color: Colors.white, size: 16),
);
}
}