// Author: fruit.com.cn import 'package:flutter/material.dart'; import '../../../core/data/models/drama.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../core/widgets/cover_play_count.dart'; import '../../../core/widgets/drama_cover_badge_ribbon.dart'; import '../../../core/widgets/remote_image.dart'; final RegExp _tagSplitter = RegExp(r'[,/、·]'); class HomeFeedCard extends StatelessWidget { const HomeFeedCard({super.key, required this.drama, required this.onTap}); final Drama drama; final VoidCallback onTap; @override Widget build(BuildContext context) { return Material( key: Key('home-feed-card-${drama.id}'), color: AppColors.transparent, child: InkWell( borderRadius: BorderRadius.circular(AppDimens.cardCoverRadius), onTap: onTap, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ _cover(context), Expanded( child: Padding( padding: const EdgeInsets.only( top: AppDimens.cardCoverTitleGap, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text( drama.localizedTitle( Localizations.localeOf(context).languageCode, ), key: Key('home-feed-title-${drama.id}'), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.left, style: const TextStyle( color: AppColors.textPrimary, fontSize: AppDimens.cardTitleFont, fontWeight: FontWeight.w500, height: AppDimens.cardTitleLineHeight, ), ), const SizedBox(height: AppDimens.cardTitleTagGap), _tags(context), ], ), ), ), ], ), ), ); } Widget _cover(BuildContext context) { final label = drama.localizedTag( Localizations.localeOf(context).languageCode, ); return ClipRRect( key: Key('home-feed-cover-clip-${drama.id}'), borderRadius: BorderRadius.circular(AppDimens.cardCoverRadius), child: AspectRatio( aspectRatio: AppDimens.cardCoverAspectRatio, child: Stack( fit: StackFit.expand, children: [ RemoteImage( drama.posterUrl, key: Key('home-feed-cover-${drama.id}'), fit: BoxFit.cover, fallbackColor: AppColors.contentSurface, ), if (label.isNotEmpty) Positioned( top: 0, right: 0, child: DramaCoverBadgeRibbon( label: label, color: drama.tagRibbonColor(AppColors.badgeHot), keyPrefix: '${drama.id}-tag', ), ), const Positioned( left: 0, right: 0, bottom: 0, child: CoverBottomScrim(), ), Positioned( right: AppDimens.cardPlayCountRight, bottom: AppDimens.cardPlayCountBottom, child: CoverPlayCount( key: Key('home-feed-views-${drama.id}'), count: drama.viewCount, ), ), ], ), ), ); } Widget _tags(BuildContext context) { final tags = _cardTags(context); if (tags.isEmpty) return const SizedBox.shrink(); return Text( tags.join(' | '), key: Key('home-feed-tags-${drama.id}'), maxLines: 1, overflow: TextOverflow.ellipsis, textAlign: TextAlign.left, style: const TextStyle( color: AppColors.textTertiary, fontSize: AppDimens.cardTagFont, letterSpacing: 0, ), ); } List _cardTags(BuildContext context) { final localized = drama.localizedTypeLabels( Localizations.localeOf(context).languageCode, ); if (localized.isNotEmpty) return localized.take(3).toList(growable: false); final seen = {}; return [drama.genre, drama.category] .expand((value) => value.split(_tagSplitter)) .map((value) => value.trim()) .where((value) => value.isNotEmpty && seen.add(value)) .take(3) .toList(growable: false); } }