import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/media/download_manager.dart'; import '../../core/theme/app_theme.dart'; import '../../models/video_item.dart'; /// 已下载视频列表(我的 → 已下载): /// 下载中任务(进度/取消)+ 已完成列表(点击播放 / 左滑或长按删除) class DownloadsScreen extends ConsumerWidget { const DownloadsScreen({super.key}); static String _fmtSize(int bytes) { if (bytes >= 1 << 30) return '${(bytes / (1 << 30)).toStringAsFixed(2)} GB'; if (bytes >= 1 << 20) return '${(bytes / (1 << 20)).toStringAsFixed(1)} MB'; return '${(bytes / 1024).toStringAsFixed(0)} KB'; } static String _fmtDuration(int seconds) { final m = seconds ~/ 60; final s = seconds % 60; return '$m:${s.toString().padLeft(2, '0')}'; } void _play(BuildContext context, DownloadItem item) { context.push('/player', extra: VideoItem( videoId: item.videoId, title: item.title, coverUrl: item.coverUrl, videoUrl: item.playUrl, duration: item.duration, author: VideoAuthor(userId: 0), )); } Future _confirmDelete( BuildContext context, WidgetRef ref, DownloadItem item) async { final ok = await showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('删除下载'), content: Text('删除「${item.title}」?'), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), child: const Text('取消')), TextButton( onPressed: () => Navigator.pop(ctx, true), child: const Text('删除', style: TextStyle(color: Colors.red))), ], ), ); if (ok == true) { ref.read(downloadManagerProvider.notifier).delete(item.videoId); } } @override Widget build(BuildContext context, WidgetRef ref) { final state = ref.watch(downloadManagerProvider); final downloading = state.progress.entries.toList(); return Scaffold( appBar: AppBar(title: const Text('已下载')), body: ListView( children: [ // 下载中 for (final e in downloading) ListTile( leading: const SizedBox( width: 56, height: 56, child: Center(child: CircularProgressIndicator(strokeWidth: 2)), ), title: Text('视频 #${e.key}', maxLines: 1, overflow: TextOverflow.ellipsis), subtitle: Padding( padding: const EdgeInsets.only(top: 6), child: LinearProgressIndicator(value: e.value), ), trailing: Text('${(e.value * 100).toStringAsFixed(0)}%'), onLongPress: () => ref.read(downloadManagerProvider.notifier).cancel(e.key), ), if (downloading.isNotEmpty) const Divider(), // 已完成 if (state.items.isEmpty && downloading.isEmpty) const Padding( padding: EdgeInsets.only(top: 120), child: Center( child: Text('暂无下载\n长按视频可下载', textAlign: TextAlign.center, style: TextStyle(color: AppColors.textWeak)), ), ), for (final item in state.items) ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), leading: ClipRRect( borderRadius: BorderRadius.circular(6), child: SizedBox( width: 72, height: 48, child: item.coverUrl.isNotEmpty ? CachedNetworkImage( imageUrl: item.coverUrl, fit: BoxFit.cover) : Container( color: Colors.grey[800], child: const Icon(Icons.play_circle_outline, color: Colors.white54), ), ), ), title: Text(item.title.isEmpty ? '视频 #${item.videoId}' : item.title, maxLines: 1, overflow: TextOverflow.ellipsis), subtitle: Text( '${_fmtDuration(item.duration)} · ${_fmtSize(item.sizeBytes)}', style: const TextStyle(fontSize: 12), ), trailing: IconButton( icon: const Icon(Icons.delete_outline, color: AppColors.textWeak), onPressed: () => _confirmDelete(context, ref, item), ), onTap: () => _play(context, item), ), ], ), ); } }