1
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
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';
|
||||
@@ -11,7 +13,7 @@ import 'video_info_section.dart';
|
||||
import 'video_player_view.dart';
|
||||
|
||||
/// Feed 单页:播放器 + 手势层 + 互动栏 + 信息区 + 双击爱心动画
|
||||
/// 手势(《01-开发文档》5.3):单击暂停/继续,双击点赞
|
||||
/// 手势(《01-开发文档》5.3):单击暂停/继续,双击点赞,长按弹倍速/分享
|
||||
class FeedVideoPage extends ConsumerStatefulWidget {
|
||||
const FeedVideoPage({
|
||||
super.key,
|
||||
@@ -22,26 +24,16 @@ class FeedVideoPage extends ConsumerStatefulWidget {
|
||||
required this.onLike,
|
||||
this.onFavorite,
|
||||
this.onComment,
|
||||
this.onShare,
|
||||
});
|
||||
|
||||
final VideoItem item;
|
||||
final int slotIndex;
|
||||
final FeedPlayerPool pool;
|
||||
final bool isCurrent;
|
||||
|
||||
/// 点赞回调(由上层接 feedProvider.toggleLike,内部判登录态)
|
||||
final void Function(int videoId) onLike;
|
||||
|
||||
/// 收藏回调(由上层接 feedProvider.toggleFavorite)
|
||||
final void Function(int videoId)? onFavorite;
|
||||
|
||||
/// 评论入口(打开评论弹层)
|
||||
final void Function(VideoItem item)? onComment;
|
||||
|
||||
/// 分享入口(系统分享面板)
|
||||
final void Function(VideoItem item)? onShare;
|
||||
|
||||
@override
|
||||
ConsumerState<FeedVideoPage> createState() => _FeedVideoPageState();
|
||||
}
|
||||
@@ -49,24 +41,98 @@ class FeedVideoPage extends ConsumerStatefulWidget {
|
||||
class _FeedVideoPageState extends ConsumerState<FeedVideoPage> {
|
||||
int _likeBurstTrigger = 0;
|
||||
|
||||
static const _rates = [1.0, 1.25, 1.5, 2.0];
|
||||
|
||||
void _onDoubleTap() {
|
||||
// 游客双击:与点赞按钮一致守卫登录(《01-开发文档》2.1.1 游客不可点赞)
|
||||
if (!ref.read(authProvider).isLoggedIn) {
|
||||
context.go('/login');
|
||||
return;
|
||||
}
|
||||
setState(() => _likeBurstTrigger++);
|
||||
// 双击:未点赞等同点赞;已点赞只播动画不改状态
|
||||
if (!widget.item.isLiked) {
|
||||
widget.onLike(widget.item.videoId);
|
||||
}
|
||||
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(
|
||||
@@ -74,10 +140,7 @@ class _FeedVideoPageState extends ConsumerState<FeedVideoPage> {
|
||||
children: [
|
||||
// 底部渐变(提升文字可读性)
|
||||
const Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
height: 160,
|
||||
left: 0, right: 0, bottom: 0, height: 160,
|
||||
child: IgnorePointer(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
@@ -91,9 +154,10 @@ class _FeedVideoPageState extends ConsumerState<FeedVideoPage> {
|
||||
),
|
||||
),
|
||||
|
||||
// 播放器
|
||||
// 播放器(含抖音风格细进度条)
|
||||
VideoPlayerView(
|
||||
controller: widget.pool.controllerAt(widget.slotIndex),
|
||||
pool: widget.pool,
|
||||
slotIndex: widget.slotIndex,
|
||||
coverUrl: widget.item.coverUrl,
|
||||
playing: widget.isCurrent && widget.pool.isCurrentPlaying,
|
||||
),
|
||||
@@ -101,24 +165,20 @@ class _FeedVideoPageState extends ConsumerState<FeedVideoPage> {
|
||||
// 双击爱心动画
|
||||
IgnorePointer(child: LikeBurst(trigger: _likeBurstTrigger)),
|
||||
|
||||
// 右侧互动栏
|
||||
// 右侧互动栏(头像/点赞/评论/收藏 — 分享已移至长按菜单)
|
||||
Positioned(
|
||||
right: 8,
|
||||
bottom: 96,
|
||||
right: 8, bottom: 96,
|
||||
child: InteractionBar(
|
||||
item: widget.item,
|
||||
onLike: widget.onLike,
|
||||
onFavorite: widget.onFavorite,
|
||||
onComment: widget.onComment,
|
||||
onShare: widget.onShare,
|
||||
),
|
||||
),
|
||||
|
||||
// 左下信息区
|
||||
Positioned(
|
||||
left: 12,
|
||||
right: 88,
|
||||
bottom: 24,
|
||||
left: 12, right: 88, bottom: 24,
|
||||
child: VideoInfoSection(item: widget.item),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user