import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:share_plus/share_plus.dart'; import '../../../core/player/feed_player_pool.dart'; import '../../../core/theme/app_theme.dart'; import '../../../models/video_item.dart'; import '../../../providers/auth_provider.dart'; import 'feed_overlays.dart'; import 'interaction_bar.dart'; import 'video_info_section.dart'; import 'video_player_view.dart'; /// Feed 单页:播放器 + 手势层 + 互动栏 + 信息区 + 双击爱心动画 /// 手势(《01-开发文档》5.3):单击暂停/继续,双击点赞,长按弹倍速/分享 class FeedVideoPage extends ConsumerStatefulWidget { const FeedVideoPage({ super.key, required this.item, required this.slotIndex, required this.pool, required this.isCurrent, required this.onLike, this.onFavorite, this.onComment, }); final VideoItem item; final int slotIndex; final FeedPlayerPool pool; final bool isCurrent; final void Function(int videoId) onLike; final void Function(int videoId)? onFavorite; final void Function(VideoItem item)? onComment; @override ConsumerState createState() => _FeedVideoPageState(); } class _FeedVideoPageState extends ConsumerState { int _likeBurstTrigger = 0; static const _rates = [1.0, 1.25, 1.5, 2.0]; void _onDoubleTap() { if (!ref.read(authProvider).isLoggedIn) { context.go('/login'); return; } setState(() => _likeBurstTrigger++); if (!widget.item.isLiked) widget.onLike(widget.item.videoId); } void _onLongPress() { showModalBottomSheet( context: context, backgroundColor: Colors.white, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (_) { final currentRate = widget.pool.slotAt(widget.slotIndex).player.state.rate; return SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: const Icon(Icons.speed, color: AppColors.primary), title: const Text('倍速播放'), subtitle: Text('当前 ${currentRate.toStringAsFixed(2)}x'), onTap: () { Navigator.of(context).pop(); _showRateSheet(); }, ), const Divider(height: 1), ListTile( leading: const Icon(Icons.share_outlined), title: const Text('分享'), onTap: () { Navigator.of(context).pop(); _share(); }, ), const SizedBox(height: 8), ], ), ); }, ); } void _showRateSheet() { final currentRate = widget.pool.slotAt(widget.slotIndex).player.state.rate; showModalBottomSheet( context: context, backgroundColor: Colors.white, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (_) => SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ for (final r in _rates) ListTile( title: Center(child: Text('${r}x')), trailing: (currentRate - r).abs() < 0.01 ? const Icon(Icons.check, color: AppColors.primary) : null, onTap: () { widget.pool.slotAt(widget.slotIndex).player.setRate(r); Navigator.of(context).pop(); }, ), const SizedBox(height: 8), ], ), ), ); } Future _share() async { final text = '${widget.item.title}\n${widget.item.videoUrl}'; await Share.share(text, subject: widget.item.title); } @override Widget build(BuildContext context) { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: widget.pool.toggleCurrent, onDoubleTap: _onDoubleTap, onLongPress: _onLongPress, child: Container( color: Colors.black, child: Stack( fit: StackFit.expand, children: [ // 底部渐变(提升文字可读性) const Positioned( left: 0, right: 0, bottom: 0, height: 160, child: IgnorePointer( child: DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.transparent, Colors.black54], ), ), ), ), ), // 播放器(含抖音风格细进度条) VideoPlayerView( pool: widget.pool, slotIndex: widget.slotIndex, coverUrl: widget.item.coverUrl, playing: widget.isCurrent && widget.pool.isCurrentPlaying, ), // 双击爱心动画 IgnorePointer(child: LikeBurst(trigger: _likeBurstTrigger)), // 右侧互动栏(头像/点赞/评论/收藏 — 分享已移至长按菜单) Positioned( right: 8, bottom: 96, child: InteractionBar( item: widget.item, onLike: widget.onLike, onFavorite: widget.onFavorite, onComment: widget.onComment, ), ), // 左下信息区 Positioned( left: 12, right: 88, bottom: 24, child: VideoInfoSection(item: widget.item), ), ], ), ), ); } }