Files
ReelShort/lib/pages/following/following_history_card.dart
T

234 lines
7.6 KiB
Dart

// Author: fruit.com.cn
import 'package:flutter/material.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_dimens.dart';
import '../../core/widgets/cover_play_count.dart';
import '../../core/widgets/remote_image.dart';
import '../../l10n/app_localizations.dart';
class FollowingHistoryCard extends StatelessWidget {
const FollowingHistoryCard({
super.key,
required this.dramaId,
required this.title,
required this.posterUrl,
required this.viewCount,
required this.subtitle,
required this.onTap,
required this.onContinue,
this.progress,
this.progressLabel,
this.editing = false,
this.onRemove,
});
final String dramaId;
final String title;
final String posterUrl;
final int viewCount;
final String subtitle;
final VoidCallback onTap;
final VoidCallback onContinue;
final double? progress;
final String? progressLabel;
final bool editing;
final VoidCallback? onRemove;
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return InkWell(
key: Key('following-history-card-$dramaId'),
borderRadius: BorderRadius.circular(
AppDimens.followingHistoryCoverRadius,
),
onTap: onTap,
child: SizedBox(
height: AppDimens.followingHistoryCoverHeight,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_cover(),
const SizedBox(width: AppDimens.followingHistoryContentGap),
Expanded(child: _details(l10n)),
],
),
),
);
}
Widget _cover() {
return SizedBox(
key: Key('following-history-cover-$dramaId'),
width: AppDimens.followingHistoryCoverWidth,
height: AppDimens.followingHistoryCoverHeight,
child: Stack(
clipBehavior: Clip.hardEdge,
children: [
Positioned.fill(
child: ClipRRect(
borderRadius: BorderRadius.circular(
AppDimens.followingHistoryCoverRadius,
),
child: RemoteImage(
posterUrl,
fit: BoxFit.cover,
fallbackColor: AppColors.contentSurface,
),
),
),
const Positioned(
left: 0,
right: 0,
bottom: 0,
child: CoverBottomScrim(),
),
Positioned(
right: AppDimens.cardPlayCountRight,
bottom: AppDimens.cardPlayCountBottom,
child: CoverPlayCount(
key: Key('following-history-views-$dramaId'),
count: viewCount,
),
),
],
),
);
}
Widget _details(AppLocalizations l10n) {
final hasProgress = progress != null;
return Stack(
children: [
Align(
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(right: editing ? 28 : 0),
child: Text(
title,
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.followingSubtitleGap),
Row(
children: [
Expanded(
child: Text(
subtitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: AppColors.textTertiary,
fontSize: AppDimens.followingSubtitleFont,
),
),
),
if (!editing && hasProgress) ...[
const SizedBox(width: AppDimens.spaceSm),
_continueButton(l10n),
],
],
),
if (hasProgress) ...[
const SizedBox(height: AppDimens.followingHistoryProgressGap),
ClipRRect(
borderRadius: BorderRadius.circular(
AppDimens.followingHistoryProgressRadius,
),
child: LinearProgressIndicator(
key: Key('following-progress-$dramaId'),
value: progress!.clamp(0.0, 1.0).toDouble(),
minHeight: AppDimens.followingHistoryProgressHeight,
backgroundColor: AppColors.inactiveTrack,
valueColor: const AlwaysStoppedAnimation<Color>(
AppColors.brandRed,
),
),
),
if (progressLabel != null) ...[
const SizedBox(height: AppDimens.followingHistoryLabelGap),
Text(
progressLabel!,
key: Key('following-history-percent-$dramaId'),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: AppColors.textTertiary,
fontSize: AppDimens.followingSubtitleFont,
),
),
],
],
],
),
),
),
if (editing)
Positioned(
top: 0,
right: 0,
child: GestureDetector(
key: Key('following-history-remove-$dramaId'),
onTap: onRemove,
child: const CircleAvatar(
radius: 12,
backgroundColor: AppColors.contentChip,
child: Icon(
Icons.remove_rounded,
color: AppColors.textPrimary,
size: 17,
),
),
),
),
],
);
}
Widget _continueButton(AppLocalizations l10n) {
return SizedBox(
height: AppDimens.followingHistoryActionHeight,
child: TextButton(
key: Key('following-history-continue-$dramaId'),
onPressed: onContinue,
style: ButtonStyle(
foregroundColor: WidgetStateProperty.resolveWith(
(states) => states.contains(WidgetState.pressed)
? AppColors.primary
: AppColors.textTabInactive,
),
backgroundColor: const WidgetStatePropertyAll(AppColors.transparent),
overlayColor: const WidgetStatePropertyAll(AppColors.transparent),
padding: const WidgetStatePropertyAll(EdgeInsets.zero),
minimumSize: const WidgetStatePropertyAll(Size.zero),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.compact,
textStyle: const WidgetStatePropertyAll(
TextStyle(
fontSize: AppDimens.followingSubtitleFont,
fontWeight: FontWeight.w500,
),
),
),
child: Text(l10n.continueWatching),
),
);
}
}