// Author: fruit.com.cn import 'package:flutter/material.dart'; import '../../../core/data/models/watch_progress.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../core/widgets/remote_image.dart'; import '../../../l10n/app_localizations.dart'; class MineContinueWatching extends StatelessWidget { const MineContinueWatching({ super.key, required this.records, required this.onOpenRecord, required this.onOpenAll, }); final List records; /// 单张卡片:直接进入该剧的播放页,并定位到记录的集。 final ValueChanged onOpenRecord; final VoidCallback onOpenAll; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final visible = records.take(10).toList(growable: false); if (visible.isEmpty) return const SizedBox.shrink(); return Column( key: const Key('mine-continue-watching'), crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric( horizontal: AppDimens.mineGridHorizontal, ), child: Row( children: [ Expanded( child: Text( l10n.continueWatching, style: const TextStyle( color: AppColors.textPrimary, fontSize: AppDimens.mineSectionTitleFont, fontWeight: FontWeight.w600, ), ), ), InkWell( key: const Key('mine-continue-all'), onTap: onOpenAll, child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( l10n.all, style: const TextStyle( color: AppColors.textTertiary, fontSize: AppDimens.mineSectionLinkFont, ), ), const Icon( Icons.chevron_right_rounded, color: AppColors.textTertiary, size: 16, ), ], ), ), ], ), ), const SizedBox(height: AppDimens.spaceSm), SizedBox( height: AppDimens.mineContinueRowHeight, child: ListView.separated( key: const Key('mine-continue-list'), scrollDirection: Axis.horizontal, physics: const BouncingScrollPhysics(), padding: const EdgeInsets.symmetric( horizontal: AppDimens.mineGridHorizontal, ), itemCount: visible.length, separatorBuilder: (_, _) => const SizedBox(width: AppDimens.spaceMd), itemBuilder: (_, index) { final record = visible[index]; return _card( l10n, record, Localizations.localeOf(context).languageCode, ); }, ), ), ], ); } Widget _card( AppLocalizations l10n, WatchProgress record, String languageCode, ) { final title = record.localizedTitle(languageCode).trim(); final episode = record.episodeNumber; final hasProgress = record.duration > Duration.zero; return SizedBox( width: AppDimens.mineContinueCardWidth, child: InkWell( key: Key('mine-continue-${record.dramaId}'), borderRadius: BorderRadius.circular(AppDimens.homeCardRadius), onTap: () => onOpenRecord(record), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ AspectRatio( aspectRatio: AppDimens.cardCoverAspectRatio, child: Stack( clipBehavior: Clip.hardEdge, children: [ Positioned.fill( child: ClipRRect( borderRadius: BorderRadius.circular( AppDimens.coverRadius, ), child: RemoteImage( record.dramaCover ?? '', fit: BoxFit.cover, fallbackColor: AppColors.contentSurface, ), ), ), if (hasProgress) Positioned( left: 0, right: 0, bottom: 0, child: LinearProgressIndicator( value: record.fraction, minHeight: AppDimens.followingProgressHeight, backgroundColor: AppColors.inactiveTrack, valueColor: const AlwaysStoppedAnimation( AppColors.primary, ), ), ), ], ), ), const SizedBox(height: AppDimens.cardCoverTitleGap), Text( title.isEmpty ? l10n.dataUnavailable : title, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( color: AppColors.textPrimary, fontSize: AppDimens.mineContinueTitleFont, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 2), Text( episode == null || episode <= 0 ? l10n.dataUnavailable : l10n.episodeLabel(episode), maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( color: AppColors.textTertiary, fontSize: AppDimens.mineContinueMetaFont, ), ), ], ), ), ); } }