import 'package:flutter/material.dart'; import '../constants/breakpoints.dart'; import 'hover_scrollable_row.dart'; import 'smart_image.dart'; class CastEntry { final String name; final String? subtitle; final String? imageUrl; final Map? imageHeaders; final VoidCallback? onTap; const CastEntry({ required this.name, this.subtitle, this.imageUrl, this.imageHeaders, this.onTap, }); } class CastScroller extends StatelessWidget { final List entries; final EdgeInsets padding; final int maxCount; const CastScroller({ super.key, required this.entries, this.padding = EdgeInsets.zero, this.maxCount = 20, }); @override Widget build(BuildContext context) { final count = entries.length.clamp(0, maxCount); final compact = MediaQuery.sizeOf(context).width < Breakpoints.compact; return SizedBox( height: compact ? 146 : 164, child: HoverScrollableRow( builder: (_, controller) => ListView.separated( controller: controller, scrollDirection: Axis.horizontal, padding: padding, itemCount: count, separatorBuilder: (_, _) => SizedBox(width: compact ? 12 : 16), itemBuilder: (_, i) => _CastCard(entry: entries[i], compact: compact), ), ), ); } } class _CastCard extends StatelessWidget { final CastEntry entry; final bool compact; const _CastCard({required this.entry, this.compact = false}); @override Widget build(BuildContext context) { final avatarSize = compact ? 60.0 : 72.0; final card = SizedBox( width: compact ? 74 : 84, child: Column( children: [ ClipRRect( borderRadius: BorderRadius.circular(avatarSize / 2), child: SizedBox( width: avatarSize, height: avatarSize, child: SmartImage( url: entry.imageUrl, httpHeaders: entry.imageHeaders, fallbackIcon: Icons.person, borderRadius: avatarSize / 2, ), ), ), SizedBox(height: compact ? 6 : 8), Text( entry.name, maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, style: TextStyle( fontSize: compact ? 11 : 12, fontWeight: FontWeight.w500, color: Colors.white, ), ), if (entry.subtitle != null && entry.subtitle!.isNotEmpty) ...[ const SizedBox(height: 2), Text( entry.subtitle!, maxLines: 1, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, style: TextStyle( fontSize: compact ? 10 : 11, color: Colors.white.withValues(alpha: 0.6), ), ), ], ], ), ); if (entry.onTap == null) return card; return MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: entry.onTap, child: card, ), ); } }