Files
ReelShort/lib/pages/recommend/widgets/recommend_section_card.dart
T

142 lines
5.0 KiB
Dart

// Author: fruit.com.cn
import 'package:flutter/material.dart';
import '../../../core/data/models/drama.dart';
import '../../../core/formatting/count_format.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_dimens.dart';
import '../../../core/widgets/drama_genre_ribbon.dart';
import '../../../core/widgets/remote_image.dart';
import '../../../l10n/app_localizations.dart';
import '../../home/widgets/home_drama_classification.dart';
final RegExp _tagSplitter = RegExp(r'[,/、·]');
class RecommendSectionCard extends StatelessWidget {
const RecommendSectionCard({
super.key,
required this.sectionKey,
required this.drama,
required this.onTap,
});
final String sectionKey;
final Drama drama;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return SizedBox(
width: AppDimens.recommendCardWidth,
child: InkWell(
key: Key('recommend-card-$sectionKey-${drama.id}'),
borderRadius: BorderRadius.circular(AppDimens.cardCoverRadius),
onTap: onTap,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(AppDimens.cardCoverRadius),
child: AspectRatio(
aspectRatio: AppDimens.cardCoverAspectRatio,
child: Stack(
fit: StackFit.expand,
children: [
RemoteImage(
drama.posterUrl,
fit: BoxFit.cover,
showFallbackIcon: true,
fallbackColor: AppColors.contentSurface,
),
if (isHotDrama(drama))
Positioned(
top: AppDimens.spaceXs,
left: AppDimens.spaceXs,
child: Container(
key: Key('recommend-hot-$sectionKey-${drama.id}'),
padding: const EdgeInsets.symmetric(
horizontal: AppDimens.cardBadgeHorizontalPadding,
vertical: AppDimens.cardBadgeVerticalPadding,
),
decoration: BoxDecoration(
color: AppColors.badgeHot,
borderRadius: BorderRadius.circular(
AppDimens.cardBadgeRadius,
),
),
child: Text(
l10n.homeBadgeHot,
style: const TextStyle(
color: AppColors.textPrimary,
fontSize: AppDimens.cardBadgeFont,
fontWeight: FontWeight.w700,
),
),
),
),
Positioned(
top: 0,
right: 0,
child: DramaGenreRibbon(
tags: _cardTags(),
keyPrefix: '$sectionKey-${drama.id}',
),
),
],
),
),
),
const SizedBox(height: AppDimens.cardCoverTitleGap),
Row(
key: Key('recommend-views-$sectionKey-${drama.id}'),
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Icon(
Icons.auto_awesome_rounded,
color: AppColors.textPrimary,
size: AppDimens.cardViewsIcon,
),
Text(
formatPlayCount(drama.viewCount),
style: const TextStyle(
color: AppColors.textPrimary,
fontSize: AppDimens.cardViewsFont,
),
),
],
),
const SizedBox(height: 2),
Text(
drama.title,
key: Key('recommend-title-$sectionKey-${drama.id}'),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: AppColors.textPrimary,
fontSize: AppDimens.cardTitleFont,
fontWeight: FontWeight.w500,
height: AppDimens.cardTitleLineHeight,
),
),
],
),
),
);
}
List<String> _cardTags() {
final seen = <String>{};
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);
}
}