feat: add MobilePosterCard widget and MobileLiveTVTab for IPTV live TV functionality.

This commit is contained in:
Michael committed 2026-03-27 15:24:10 +01:00
1 parent a1b1363bc8
commit f07a6e91f9
2 files changed
+93 -96

No files matched your search

@@ -197,10 +197,10 @@ class _MobileLiveTVTabState extends ConsumerState<MobileLiveTVTab>
slivers: [
// Floating Header (Search + Navigation)
SliverAppBar(
floating: true,
pinned: false,
snap: true,
backgroundColor: Colors.transparent, // Minimal background
floating: false,
pinned: true,
snap: false,
backgroundColor: const Color(0xFF000000), // Opaque black
elevation: 0,
scrolledUnderElevation: 0,
automaticallyImplyLeading: false,
+89 -92
View File
@@ -30,8 +30,9 @@ class MobilePosterCard extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Shadow lives on this outer Container so ClipRRect doesn't swallow it
Expanded(
// FIXED Aspect Ratio for the image part to avoid stretching
AspectRatio(
aspectRatio: 2 / 3,
child: Container(
decoration: AppDecorations.glossyCard(context, radius: 12),
child: TVFocusable(
@@ -48,97 +49,32 @@ class MobilePosterCard extends StatelessWidget {
? CachedNetworkImage(
imageUrl: imageUrl!,
fit: BoxFit.cover,
memCacheWidth: 120,
memCacheHeight: 180,
maxWidthDiskCache: 120,
maxHeightDiskCache: 180,
alignment: Alignment.center,
memCacheWidth: 200, // Increased for better quality
memCacheHeight: 300,
maxWidthDiskCache: 400,
maxHeightDiskCache: 600,
cacheManager: AppCacheManager.instance,
errorWidget: (_, __, ___) => Container(
decoration:
AppDecorations.channelCardBase(context),
child: Icon(
placeholderIcon,
color: AppDecorations.iconMuted(context),
),
),
placeholder: (_, __) => Container(
decoration:
AppDecorations.channelCardBase(context),
child: Icon(
placeholderIcon,
color: AppDecorations.iconMuted(context),
),
),
errorWidget: (_, __, ___) => _buildPlaceholder(context),
placeholder: (_, __) => _buildPlaceholder(context),
)
: Container(
decoration: AppDecorations.channelCardBase(context),
child: Icon(
placeholderIcon,
color: AppDecorations.iconMuted(context),
),
),
: _buildPlaceholder(context),
),
// Rating badge
if (rating != null)
Positioned(
top: 6,
left: 6,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 4,
vertical: 2,
),
decoration: BoxDecoration(
color: Colors.black87,
borderRadius: BorderRadius.circular(4),
),
child: Row(
children: [
const Icon(
Icons.star,
color: Colors.amber,
size: 10,
),
const SizedBox(width: 2),
Text(
rating!,
style: const TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
if (rating != null && rating!.isNotEmpty)
_buildRatingBadge(),
// Watched badge
if (isWatched)
Positioned(
top: 6,
right: 6,
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: Color(0xFF30D158),
shape: BoxShape.circle,
),
child: const Icon(
Icons.check,
size: 10,
color: Colors.white,
),
),
),
_buildWatchedBadge(),
// Glossy highlight overlay (top shimmer)
Positioned(
top: 0,
left: 0,
right: 0,
height: 80, // Taller for posters
height: 60,
child: IgnorePointer(
child: Container(
decoration: AppDecorations.glossShimmer(context, radius: 12),
@@ -151,24 +87,85 @@ class MobilePosterCard extends StatelessWidget {
),
),
const SizedBox(height: 6),
const SizedBox(height: 4),
// Title
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Text(
title,
textAlign: TextAlign.center,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: AppDecorations.textPrimary(context),
fontSize: 12,
fontWeight: FontWeight.w500,
// Title with fixed height to avoid pushing the image
SizedBox(
height: 16,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Text(
title,
textAlign: TextAlign.center,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: AppDecorations.textPrimary(context),
fontSize: 11,
fontWeight: FontWeight.w500,
),
),
),
),
],
);
}
Widget _buildPlaceholder(BuildContext context) {
return Container(
decoration: AppDecorations.channelCardBase(context),
child: Icon(
placeholderIcon,
color: AppDecorations.iconMuted(context),
),
);
}
Widget _buildRatingBadge() {
return Positioned(
top: 6,
left: 6,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
decoration: BoxDecoration(
color: Colors.black87,
borderRadius: BorderRadius.circular(4),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.star, color: Colors.amber, size: 10),
const SizedBox(width: 2),
Text(
rating!,
style: const TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
Widget _buildWatchedBadge() {
return Positioned(
top: 6,
right: 6,
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: Color(0xFF30D158),
shape: BoxShape.circle,
),
child: const Icon(
Icons.check,
size: 10,
color: Colors.white,
),
),
);
}
}