Files
video/app/lib/screens/feed/widgets/feed_video_page.dart
T
2026-10-16 16:05:01 +08:00

190 lines
5.9 KiB
Dart

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<FeedVideoPage> createState() => _FeedVideoPageState();
}
class _FeedVideoPageState extends ConsumerState<FeedVideoPage> {
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<void>(
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<void>(
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<void> _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),
),
],
),
),
);
}
}