import 'package:flutter/material.dart'; import '../../core/contracts/library.dart'; import '../theme/app_theme.dart'; import 'hover_scrollable_row.dart'; import 'media_row_metrics.dart'; import 'media_poster_card.dart'; class LatestMediaRow extends StatefulWidget { final String title; final String? subtitle; final List items; final String? Function(EmbyRawItem) imageUrlBuilder; final String? Function(EmbyRawItem)? preloadImageUrlBuilder; final void Function(EmbyRawItem) onItemTap; final VoidCallback? onSeeAll; final int? totalCount; final MediaCardVariant cardVariant; final bool enableContextMenu; final Map? favoriteMap; final Map? playedMap; final Map? favoriteLoadingMap; final Map? playedLoadingMap; final void Function(EmbyRawItem)? onAddFavorite; final void Function(EmbyRawItem)? onMarkPlayed; final void Function(EmbyRawItem)? onHideFromResume; final Map? hideFromResumeLoadingMap; final bool showUnplayedCountBadge; final bool showHoverPlayIcon; final String? activeItemId; final String? fallbackImageUrl; final Map? imageHeaders; final Color? titleColor; const LatestMediaRow({ super.key, required this.title, this.subtitle, required this.items, required this.imageUrlBuilder, this.preloadImageUrlBuilder, required this.onItemTap, this.onSeeAll, this.totalCount, this.cardVariant = MediaCardVariant.poster, this.enableContextMenu = false, this.favoriteMap, this.playedMap, this.favoriteLoadingMap, this.playedLoadingMap, this.onAddFavorite, this.onMarkPlayed, this.onHideFromResume, this.hideFromResumeLoadingMap, this.showUnplayedCountBadge = true, this.showHoverPlayIcon = true, this.activeItemId, this.fallbackImageUrl, this.imageHeaders, this.titleColor, }); @override State createState() => _LatestMediaRowState(); } class _LatestMediaRowState extends State { @override Widget build(BuildContext context) { final compact = MediaRowMetrics.isCompact(context); final rowHeight = MediaRowMetrics.rowHeight( widget.cardVariant, compact: compact, ); final cardWidth = MediaRowMetrics.cardWidth( widget.cardVariant, compact: compact, ); final theme = Theme.of(context); final tokens = context.appTokens; final titleColor = widget.titleColor; final mutedColor = titleColor != null ? titleColor.withValues(alpha: 0.55) : tokens.mutedForeground; final seeAllColor = titleColor != null ? titleColor.withValues(alpha: 0.78) : theme.colorScheme.onSurface.withValues(alpha: 0.82); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.fromLTRB(24, 12, 24, 10), child: Row( children: [ Expanded( child: Row( children: [ Flexible( child: Text( widget.title, maxLines: 1, overflow: TextOverflow.ellipsis, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w700, color: titleColor, ), ), ), if (widget.subtitle != null) ...[ const SizedBox(width: 8), Text( '· ${widget.subtitle}', maxLines: 1, overflow: TextOverflow.ellipsis, style: theme.textTheme.bodySmall?.copyWith( color: mutedColor, ), ), ], ], ), ), if (widget.onSeeAll != null) TextButton( onPressed: widget.onSeeAll, style: TextButton.styleFrom( foregroundColor: seeAllColor, padding: const EdgeInsets.symmetric(horizontal: 8), visualDensity: VisualDensity.compact, ), child: Text( widget.totalCount != null && widget.totalCount! > 0 ? '查看全部 ${widget.totalCount}' : '查看全部', ), ), ], ), ), SizedBox( height: rowHeight, child: HoverScrollableRow( builder: (_, controller) => ListView.separated( controller: controller, scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 24), itemCount: widget.items.length, separatorBuilder: (_, index) => const SizedBox(width: 14), itemBuilder: (_, index) { final item = widget.items[index]; final isActive = widget.activeItemId == item.Id; return MediaPosterCard( item: item, imageUrl: widget.imageUrlBuilder(item), onTap: () => widget.onItemTap(item), width: cardWidth, variant: widget.cardVariant, showHoverPlayIcon: widget.showHoverPlayIcon, enableContextMenu: widget.enableContextMenu, isFavoriteOverride: widget.favoriteMap?[item.Id] ?? item.UserData?.IsFavorite, isPlayedOverride: widget.playedMap?[item.Id] ?? item.UserData?.Played, favoriteLoading: widget.favoriteLoadingMap?[item.Id] ?? false, playedLoading: widget.playedLoadingMap?[item.Id] ?? false, onAddFavorite: widget.onAddFavorite != null ? () => widget.onAddFavorite!(item) : null, onMarkPlayed: widget.onMarkPlayed != null ? () => widget.onMarkPlayed!(item) : null, onHideFromResume: widget.onHideFromResume != null ? () => widget.onHideFromResume!(item) : null, hideFromResumeLoading: widget.hideFromResumeLoadingMap?[item.Id] ?? false, showUnplayedCountBadge: widget.showUnplayedCountBadge, isActive: isActive, fallbackImageUrl: widget.fallbackImageUrl, preloadImageUrl: widget.preloadImageUrlBuilder?.call(item), imageHeaders: widget.imageHeaders, textColor: widget.titleColor, ); }, ), ), ), ], ); } }