修复竖屏全屏尺寸、返回,手势切换问题,还原pinnedHeader

This commit is contained in:
orz12
2023-12-19 12:16:10 +08:00
parent 4d07f1508a
commit 16705f008c
2 changed files with 150 additions and 136 deletions

View File

@ -234,41 +234,43 @@ class _VideoDetailPageState extends State<VideoDetailPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final videoHeight = MediaQuery.of(context).size.width * 9 / 16; final videoHeight = MediaQuery.of(context).size.width * 9 / 16;
// final double pinnedHeaderHeight = final double pinnedHeaderHeight =
// statusBarHeight + kToolbarHeight + videoHeight; statusBarHeight + kToolbarHeight + videoHeight;
if (MediaQuery.of(context).orientation == Orientation.landscape) { if (MediaQuery.of(context).orientation == Orientation.landscape ||
plPlayerController!.isFullScreen.value) {
enterFullScreen(); enterFullScreen();
} else { } else {
exitFullScreen(); exitFullScreen();
} }
Widget childWhenDisabled = PopScope( Widget childWhenDisabled = SafeArea(
canPop: !plPlayerController!.isFullScreen.value, top: MediaQuery.of(context).orientation == Orientation.portrait,
onPopInvoked: (bool didPop) { bottom: MediaQuery.of(context).orientation == Orientation.portrait,
if (plPlayerController!.isFullScreen.value) { left: !plPlayerController!.isFullScreen.value,
plPlayerController!.triggerFullScreen(status: false); right: !plPlayerController!.isFullScreen.value,
} child: Stack(
if (MediaQuery.of(context).orientation == Orientation.landscape) { children: [
verticalScreen(); Scaffold(
} resizeToAvoidBottomInset: false,
}, key: videoDetailController.scaffoldKey,
child: SafeArea( backgroundColor: Colors.black,
top: MediaQuery.of(context).orientation == Orientation.portrait, body: ExtendedNestedScrollView(
bottom: MediaQuery.of(context).orientation == Orientation.portrait, controller: _extendNestCtr,
left: !plPlayerController!.isFullScreen.value, headerSliverBuilder:
right: !plPlayerController!.isFullScreen.value, (BuildContext context, bool innerBoxIsScrolled) {
child: Stack( return <Widget>[
children: [ Obx(() => PopScope(
Scaffold( canPop: !plPlayerController!.isFullScreen.value,
resizeToAvoidBottomInset: false, onPopInvoked: (bool didPop) {
key: videoDetailController.scaffoldKey, if (plPlayerController!.isFullScreen.value) {
backgroundColor: Colors.black, plPlayerController!
body: ExtendedNestedScrollView( .triggerFullScreen(status: false);
controller: _extendNestCtr, }
headerSliverBuilder: if (MediaQuery.of(context).orientation ==
(BuildContext context, bool innerBoxIsScrolled) { Orientation.landscape) {
return <Widget>[ verticalScreen();
Obx( }
() => SliverAppBar( },
child: SliverAppBar(
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
pinned: false, pinned: false,
elevation: 0, elevation: 0,
@ -278,7 +280,11 @@ class _VideoDetailPageState extends State<VideoDetailPage>
plPlayerController!.isFullScreen.value || plPlayerController!.isFullScreen.value ||
MediaQuery.of(context).orientation == MediaQuery.of(context).orientation ==
Orientation.landscape Orientation.landscape
? MediaQuery.of(context).size.height ? MediaQuery.of(context).size.height -
(MediaQuery.of(context).orientation ==
Orientation.landscape
? 0
: statusBarHeight)
: videoHeight, : videoHeight,
backgroundColor: Colors.black, backgroundColor: Colors.black,
flexibleSpace: FlexibleSpaceBar( flexibleSpace: FlexibleSpaceBar(
@ -416,116 +422,114 @@ class _VideoDetailPageState extends State<VideoDetailPage>
}, },
), ),
)), )),
), )),
]; ];
}, },
// pinnedHeaderSliverHeightBuilder: () { // pinnedHeaderSliverHeightBuilder: () {
// return playerStatus != PlayerStatus.playing // return playerStatus != PlayerStatus.playing
// ? statusBarHeight + kToolbarHeight // ? statusBarHeight + kToolbarHeight
// : pinnedHeaderHeight; // : pinnedHeaderHeight;
// }, // },
/// 不收回 /// 不收回
// pinnedHeaderSliverHeightBuilder: () { pinnedHeaderSliverHeightBuilder: () {
// return pinnedHeaderHeight; return pinnedHeaderHeight;
// }, },
onlyOneScrollInBody: true, onlyOneScrollInBody: true,
body: Container( body: Container(
key: Key(heroTag), key: Key(heroTag),
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
child: Column( child: Column(
children: [ children: [
Opacity( Opacity(
opacity: 0, opacity: 0,
child: SizedBox( child: SizedBox(
width: double.infinity, width: double.infinity,
height: 0, height: 0,
child: Obx( child: Obx(
() => TabBar( () => TabBar(
controller: videoDetailController.tabCtr,
dividerColor: Colors.transparent,
indicatorColor:
Theme.of(context).colorScheme.background,
tabs: videoDetailController.tabs
.map((String name) => Tab(text: name))
.toList(),
),
),
),
),
Expanded(
child: TabBarView(
controller: videoDetailController.tabCtr, controller: videoDetailController.tabCtr,
children: [ dividerColor: Colors.transparent,
Builder( indicatorColor:
builder: (context) { Theme.of(context).colorScheme.background,
return CustomScrollView( tabs: videoDetailController.tabs
key: const PageStorageKey<String>('简介'), .map((String name) => Tab(text: name))
slivers: <Widget>[ .toList(),
if (videoDetailController.videoType ==
SearchType.video) ...[
const VideoIntroPanel(),
] else if (videoDetailController
.videoType ==
SearchType.media_bangumi) ...[
Obx(() => BangumiIntroPanel(
cid: videoDetailController
.cid.value)),
],
// if (videoDetailController.videoType ==
// SearchType.video) ...[
// SliverPersistentHeader(
// floating: true,
// pinned: true,
// delegate: SliverHeaderDelegate(
// height: 50,
// child:
// const MenuRow(loadingStatus: false),
// ),
// ),
// ],
SliverToBoxAdapter(
child: Divider(
indent: 12,
endIndent: 12,
color: Theme.of(context)
.dividerColor
.withOpacity(0.06),
),
),
const RelatedVideoPanel(),
],
);
},
),
VideoReplyPanel(
bvid: videoDetailController.bvid,
)
],
), ),
), ),
], ),
), ),
), Expanded(
child: TabBarView(
controller: videoDetailController.tabCtr,
children: [
Builder(
builder: (context) {
return CustomScrollView(
key: const PageStorageKey<String>('简介'),
slivers: <Widget>[
if (videoDetailController.videoType ==
SearchType.video) ...[
const VideoIntroPanel(),
] else if (videoDetailController.videoType ==
SearchType.media_bangumi) ...[
Obx(() => BangumiIntroPanel(
cid: videoDetailController.cid.value)),
],
// if (videoDetailController.videoType ==
// SearchType.video) ...[
// SliverPersistentHeader(
// floating: true,
// pinned: true,
// delegate: SliverHeaderDelegate(
// height: 50,
// child:
// const MenuRow(loadingStatus: false),
// ),
// ),
// ],
SliverToBoxAdapter(
child: Divider(
indent: 12,
endIndent: 12,
color: Theme.of(context)
.dividerColor
.withOpacity(0.06),
),
),
const RelatedVideoPanel(),
],
);
},
),
VideoReplyPanel(
bvid: videoDetailController.bvid,
)
],
),
),
],
), ),
), ),
),
/// 重新进入会刷新
// 播放完成/暂停播放
// StreamBuilder(
// stream: appbarStream.stream,
// initialData: 0,
// builder: ((context, snapshot) {
// return ScrollAppBar(
// snapshot.data!.toDouble(),
// () => continuePlay(),
// playerStatus,
// null,
// );
// }),
// )
],
), ),
));
/// 重新进入会刷新
// 播放完成/暂停播放
// StreamBuilder(
// stream: appbarStream.stream,
// initialData: 0,
// builder: ((context, snapshot) {
// return ScrollAppBar(
// snapshot.data!.toDouble(),
// () => continuePlay(),
// playerStatus,
// null,
// );
// }),
// )
],
),
);
Widget childWhenEnabled = FutureBuilder( Widget childWhenEnabled = FutureBuilder(
key: Key(heroTag), key: Key(heroTag),
future: _futureBuilderFuture, future: _futureBuilderFuture,

View File

@ -25,6 +25,7 @@ import 'widgets/backward_seek.dart';
import 'widgets/bottom_control.dart'; import 'widgets/bottom_control.dart';
import 'widgets/common_btn.dart'; import 'widgets/common_btn.dart';
import 'widgets/forward_seek.dart'; import 'widgets/forward_seek.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
class PLVideoPlayer extends StatefulWidget { class PLVideoPlayer extends StatefulWidget {
final PlPlayerController controller; final PlPlayerController controller;
@ -76,6 +77,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
late bool enableBackgroundPlay; late bool enableBackgroundPlay;
late double screenWidth; late double screenWidth;
// 用于记录上一次全屏切换手势触发时间,避免误触
DateTime? lastFullScreenToggleTime;
void onDoubleTapSeekBackward() { void onDoubleTapSeekBackward() {
_ctr.onDoubleTapSeekBackward(); _ctr.onDoubleTapSeekBackward();
} }
@ -503,11 +507,15 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
final tapPosition = details.localPosition.dx; final tapPosition = details.localPosition.dx;
final sectionWidth = totalWidth / 3; final sectionWidth = totalWidth / 3;
final delta = details.delta.dy; final delta = details.delta.dy;
/// 锁定时禁用 /// 锁定时禁用
if (_.controlsLock.value) { if (_.controlsLock.value) {
return; return;
} }
if (lastFullScreenToggleTime != null &&
DateTime.now().difference(lastFullScreenToggleTime!) <
const Duration(milliseconds: 500)) {
return;
}
if (tapPosition < sectionWidth) { if (tapPosition < sectionWidth) {
// 左边区域 👈 // 左边区域 👈
double level = (_.isFullScreen.value double level = (_.isFullScreen.value
@ -523,12 +531,14 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
const double threshold = 7.0; // 滑动阈值 const double threshold = 7.0; // 滑动阈值
if (dy > _distance && dy > threshold) { if (dy > _distance && dy > threshold) {
if (_.isFullScreen.value) { if (_.isFullScreen.value) {
lastFullScreenToggleTime = DateTime.now();
// 下滑退出全屏 // 下滑退出全屏
await widget.controller.triggerFullScreen(status: false); await widget.controller.triggerFullScreen(status: false);
} }
_distance = 0.0; _distance = 0.0;
} else if (dy < _distance && dy < -threshold) { } else if (dy < _distance && dy < -threshold) {
if (!_.isFullScreen.value) { if (!_.isFullScreen.value) {
lastFullScreenToggleTime = DateTime.now();
// 上滑进入全屏 // 上滑进入全屏
await widget.controller.triggerFullScreen(); await widget.controller.triggerFullScreen();
} }