Files
ReelShort/lib/pages/home/widgets/home_feed_card.dart
T

148 lines
4.7 KiB
Dart

// 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<String> _cardTags(BuildContext context) {
final localized = drama.localizedTypeLabels(
Localizations.localeOf(context).languageCode,
);
if (localized.isNotEmpty) return localized.take(3).toList(growable: false);
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);
}
}