132 lines
4.0 KiB
Dart
132 lines
4.0 KiB
Dart
// Author: fruit.com.cn
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import '../../../core/data/models/drama.dart';
|
|
import '../../../core/data/models/watch_progress.dart';
|
|
import '../../../core/theme/app_colors.dart';
|
|
import '../../../core/widgets/remote_image.dart';
|
|
import '../../../l10n/app_localizations.dart';
|
|
|
|
/// 「我的」页唯一保留的续看入口。
|
|
class ContinueWatchingCard extends StatelessWidget {
|
|
const ContinueWatchingCard({
|
|
super.key,
|
|
required this.drama,
|
|
required this.record,
|
|
required this.onResume,
|
|
this.cardKey = const Key('mine-continue-watching'),
|
|
this.playButtonKey = const Key('mine-continue-play'),
|
|
this.progressKey,
|
|
this.showRemaining = false,
|
|
});
|
|
|
|
final Drama drama;
|
|
final WatchProgress record;
|
|
final VoidCallback onResume;
|
|
final Key cardKey;
|
|
final Key playButtonKey;
|
|
final Key? progressKey;
|
|
final bool showRemaining;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
return Container(
|
|
key: cardKey,
|
|
margin: const EdgeInsets.symmetric(horizontal: 14),
|
|
padding: const EdgeInsets.all(9),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.card,
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
ClipRRect(
|
|
borderRadius: BorderRadius.circular(7),
|
|
child: RemoteImage(drama.posterUrl, width: 50, height: 64),
|
|
),
|
|
const SizedBox(width: 9),
|
|
Expanded(child: _info(l10n)),
|
|
const SizedBox(width: 8),
|
|
SizedBox(
|
|
width: 60,
|
|
height: 28,
|
|
child: FilledButton(
|
|
key: playButtonKey,
|
|
onPressed: onResume,
|
|
style: FilledButton.styleFrom(
|
|
backgroundColor: AppColors.blue,
|
|
foregroundColor: AppColors.onPrimary,
|
|
padding: EdgeInsets.zero,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(9),
|
|
),
|
|
textStyle: const TextStyle(fontSize: 10),
|
|
),
|
|
child: Text(l10n.resume),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _info(AppLocalizations l10n) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
l10n.continueWatching,
|
|
style: const TextStyle(
|
|
color: AppColors.accent,
|
|
fontSize: 9,
|
|
fontWeight: FontWeight.w800,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
drama.title,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(
|
|
color: AppColors.onSurface,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w800,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
_progressText(l10n),
|
|
key: progressKey,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(color: AppColors.muted, fontSize: 9),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
String _progressText(AppLocalizations l10n) {
|
|
final episode = drama.episodes.where((item) => item.id == record.episodeId);
|
|
if (showRemaining) {
|
|
final number = episode.isEmpty ? 1 : episode.first.number;
|
|
final remaining = record.duration - record.position;
|
|
final minutes = remaining.inSeconds <= 0
|
|
? 0
|
|
: (remaining.inSeconds / Duration.secondsPerMinute).ceil();
|
|
return l10n.episodeRemaining(number, minutes);
|
|
}
|
|
final label = episode.isEmpty
|
|
? l10n.episodeCount(drama.episodeCount)
|
|
: l10n.episodeOfTotal(episode.first.number, drama.episodeCount);
|
|
return '$label · ${_clock(record.position)} / ${_clock(record.duration)}';
|
|
}
|
|
}
|
|
|
|
String _clock(Duration value) {
|
|
final minutes = value.inMinutes.remainder(60).toString().padLeft(2, '0');
|
|
final seconds = value.inSeconds.remainder(60).toString().padLeft(2, '0');
|
|
return '$minutes:$seconds';
|
|
}
|