Show season posters on tab in show view

This commit is contained in:
Micah Morrison
2026-04-26 15:11:24 -04:00
parent 19a0128363
commit 310d4b46b2
2 changed files with 65 additions and 8 deletions
+34
View File
@@ -1796,6 +1796,39 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
final season = _seasons[index];
final contextMenuKey = _seasonContextMenuKeys.putIfAbsent(index, () => GlobalKey<MediaContextMenuState>());
Offset? tapPosition;
final posterPath = season.thumb;
Widget? topImage;
if (posterPath != null && posterPath.isNotEmpty) {
const posterWidth = 72.0;
const posterHeight = 108.0;
final dpr = PlexImageHelper.effectiveDevicePixelRatio(context);
final client = _getClientForMetadata(context);
final imageUrl = PlexImageHelper.getOptimizedImageUrl(
client: client,
thumbPath: posterPath,
maxWidth: posterWidth,
maxHeight: posterHeight,
devicePixelRatio: dpr,
imageType: ImageType.poster,
);
final (memWidth, _) = PlexImageHelper.getMemCacheDimensions(
displayWidth: (posterWidth * dpr).round(),
displayHeight: (posterHeight * dpr).round(),
imageType: ImageType.poster,
);
topImage = SizedBox(
width: posterWidth,
height: posterHeight,
child: CachedNetworkImage(
imageUrl: imageUrl,
cacheManager: PlexImageCacheManager.instance,
fit: BoxFit.cover,
memCacheWidth: memWidth,
placeholder: (context, url) => const PlaceholderContainer(),
errorWidget: (context, url, error) => const PlaceholderContainer(),
),
);
}
return Padding(
padding: const EdgeInsets.only(right: 8),
child: MediaContextMenu(
@@ -1819,6 +1852,7 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
child: FocusableTabChip(
label: season.title!,
isSelected: index == _selectedSeasonIndex,
topImage: topImage,
focusNode: _seasonTabFocusNodes.length > index ? _seasonTabFocusNodes[index] : null,
onSelect: () {
if (index == _selectedSeasonIndex) return;
+31 -8
View File
@@ -36,6 +36,10 @@ class FocusableTabChip extends StatefulWidget {
/// Called when SELECT key is held (D-pad long press).
final VoidCallback? onLongPress;
/// Optional image to show above the label (e.g. a poster).
/// When provided, the chip lays out vertically with image on top, label below.
final Widget? topImage;
const FocusableTabChip({
super.key,
required this.label,
@@ -47,6 +51,7 @@ class FocusableTabChip extends StatefulWidget {
this.onNavigateDown,
this.onBack,
this.onLongPress,
this.topImage,
});
@override
@@ -123,20 +128,38 @@ class _FocusableTabChipState extends State<FocusableTabChip> with FocusableChipS
final isHighlighted = showFocus || widget.isSelected;
final label = Text(
widget.label,
style: Theme.of(context).textTheme.labelLarge?.copyWith(
color: foregroundColor,
fontWeight: isHighlighted ? FontWeight.w600 : FontWeight.normal,
),
);
final hasImage = widget.topImage != null;
return FocusBuilders.buildFocusableChip(
context: context,
focusNode: focusNode,
onKeyEvent: _handleKeyEvent,
onTap: widget.onSelect,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: hasImage
? const EdgeInsets.all(8)
: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
backgroundColor: backgroundColor,
child: Text(
widget.label,
style: Theme.of(context).textTheme.labelLarge?.copyWith(
color: foregroundColor,
fontWeight: isHighlighted ? FontWeight.w600 : FontWeight.normal,
),
),
borderRadius: hasImage ? 12 : 20,
child: hasImage
? Column(
mainAxisSize: MainAxisSize.min,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(6),
child: widget.topImage!,
),
const SizedBox(height: 6),
label,
],
)
: label,
);
}
}