// Author: fruit.com.cn import 'package:flutter/material.dart'; import '../../../core/data/models/watch_record.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 ContinueWatchingBanner extends StatelessWidget { const ContinueWatchingBanner({ super.key, required this.record, required this.languageCode, required this.onTap, }); final WatchRecord record; final String languageCode; final VoidCallback onTap; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final episode = record.episodeNumber; final total = record.progress.totalEpisodes ?? record.drama?.episodeCount ?? 0; final title = record.localizedDisplayTitle(languageCode); final showMeta = episode > 0 && total > 0; return SizedBox( height: AppDimens.followingBannerHeight, child: ClipRRect( borderRadius: BorderRadius.circular(AppDimens.followingBannerRadius), child: Stack( fit: StackFit.expand, children: [ RemoteImage( record.displayLandscapeCover, fit: BoxFit.cover, fallbackColor: AppColors.contentSurface, ), const DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [ Color(0x80000000), Color(0x33000000), Color(0x00000000), ], stops: [0, 0.45, 1], ), ), ), Material( type: MaterialType.transparency, child: InkWell( key: const Key('following-continue-banner'), onTap: onTap, child: Padding( padding: const EdgeInsets.all( AppDimens.followingBannerInnerGap, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ if (title.isNotEmpty) Text( title, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( color: AppColors.bannerTitleWarm, fontSize: AppDimens.followingBannerTitleFont, fontWeight: FontWeight.w400, ), ), if (showMeta) ...[ const SizedBox( height: AppDimens.followingBannerMetaGap, ), Text( l10n.continueWatchingSubtitle(episode, total), maxLines: 1, style: const TextStyle( color: Color(0xB3F5EFE4), fontSize: AppDimens.followingBannerSubtitleFont, ), ), ], const SizedBox( height: AppDimens.followingBannerButtonTopGap, ), _ContinueButton(label: l10n.continueWatching), ], ), ), const SizedBox(height: 10), SizedBox( width: double.infinity, child: _ProgressBar(fraction: record.progress.fraction), ), ], ), ), ), ), ], ), ), ); } } class _ContinueButton extends StatelessWidget { const _ContinueButton({required this.label}); final String label; @override Widget build(BuildContext context) { return DecoratedBox( decoration: BoxDecoration( color: AppColors.brandRed, borderRadius: BorderRadius.circular( AppDimens.followingBannerButtonRadius, ), ), child: SizedBox( height: AppDimens.followingBannerButtonHeight, child: Padding( padding: const EdgeInsets.symmetric( horizontal: AppDimens.followingBannerButtonHorizontalPadding, ), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon( Icons.play_arrow_rounded, size: AppDimens.followingBannerButtonIcon, color: AppColors.onPrimary, ), const SizedBox(width: AppDimens.followingBannerButtonGap), Text( label, style: const TextStyle( color: AppColors.onPrimary, fontSize: AppDimens.followingBannerButtonFont, fontWeight: FontWeight.w400, ), ), ], ), ), ), ); } } class _ProgressBar extends StatelessWidget { const _ProgressBar({required this.fraction}); final double fraction; @override Widget build(BuildContext context) { // SizedBox 给紧约束让 DecoratedBox 轨道撑满整宽; // 不能用无子 ColoredBox 直接放 Stack(会缩成 0×0 隐形)。 return ClipRRect( borderRadius: BorderRadius.circular( AppDimens.followingBannerProgressHeight, ), child: SizedBox( height: AppDimens.followingBannerProgressHeight, width: double.infinity, child: DecoratedBox( decoration: const BoxDecoration(color: Color(0x66FFFFFF)), child: Align( alignment: Alignment.centerLeft, child: FractionallySizedBox( widthFactor: fraction.clamp(0.0, 1.0).toDouble(), // 无 child 的 ColoredBox 在松约束下会缩成 0×0,高度必须显式占满。 heightFactor: 1.0, child: const ColoredBox(color: AppColors.brandRed), ), ), ), ), ); } }