diff --git a/lib/common/widgets/list_sheet.dart b/lib/common/widgets/list_sheet.dart index bbf7b0b04..76819d971 100644 --- a/lib/common/widgets/list_sheet.dart +++ b/lib/common/widgets/list_sheet.dart @@ -109,9 +109,18 @@ class _ListSheetContentState extends State _indexStream?.add(_ctr?.index); } + late bool _isInit = true; + @override void initState() { super.initState(); + if (GStorage.collapsibleVideoPage) { + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + _isInit = false; + }); + }); + } if (_isList) { _indexStream ??= StreamController.broadcast(); _ctr = TabController( @@ -127,7 +136,7 @@ class _ListSheetContentState extends State reverse = _isList ? List.generate(widget.season.sections.length, (_) => false) : [false]; - if (widget.bvid != null && widget.season != null) { + if (GStorage.isLogin && widget.bvid != null && widget.season != null) { _favStream ??= StreamController(); () async { dynamic result = await VideoHttp.videoRelation(bvid: widget.bvid); @@ -278,6 +287,12 @@ class _ListSheetContentState extends State @override Widget build(BuildContext context) { + if (GStorage.collapsibleVideoPage && _isInit) { + return CustomScrollView( + physics: const NeverScrollableScrollPhysics(), + ); + } + return Column( children: [ Container( @@ -473,6 +488,7 @@ class _ListSheetContentState extends State ), reverse: reverse[i ?? 0], itemCount: episodes.length, + physics: const AlwaysScrollableScrollPhysics(), itemBuilder: (BuildContext context, int index) { return buildEpisodeListItem( episodes[index], diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index bd865850a..93b643612 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -22,6 +22,7 @@ import 'package:PiliPlus/pages/search/widgets/search_text.dart'; import 'package:PiliPlus/pages/video/detail/introduction/controller.dart'; import 'package:PiliPlus/pages/video/detail/related/controller.dart'; import 'package:PiliPlus/pages/video/detail/reply/controller.dart'; +import 'package:PiliPlus/pages/video/detail/view_v.dart' show ViewPointsPage; import 'package:PiliPlus/pages/video/detail/widgets/send_danmaku_panel.dart'; import 'package:PiliPlus/pages/video/detail/widgets/watch_later_list.dart'; import 'package:PiliPlus/utils/extension.dart'; @@ -1329,8 +1330,10 @@ class VideoDetailController extends GetxController } else { childKey.currentState?.showBottomSheet( enableDrag: false, - (context) => _postPanel(), backgroundColor: Colors.transparent, + (context) => ViewPointsPage( + child: _postPanel(), + ), ); } } @@ -1496,6 +1499,8 @@ class VideoDetailController extends GetxController ? Stack( children: [ SingleChildScrollView( + controller: ScrollController(), + physics: const AlwaysScrollableScrollPhysics(), child: Column( children: [ ...List.generate( diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index cfa6fb60d..d0b049bd9 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -107,7 +107,7 @@ class _VideoReplyPanelState extends State ? null : _videoReplyController.scrollController, physics: widget.needController == false - ? const NeverScrollableScrollPhysics( + ? const AlwaysScrollableScrollPhysics( parent: ClampingScrollPhysics(), ) : const AlwaysScrollableScrollPhysics(), diff --git a/lib/pages/video/detail/view_v.dart b/lib/pages/video/detail/view_v.dart index c5c964c28..dc3a3d52f 100644 --- a/lib/pages/video/detail/view_v.dart +++ b/lib/pages/video/detail/view_v.dart @@ -1796,13 +1796,13 @@ class _VideoDetailPageVState extends State ); Widget videoIntro([bool needRelated = true, bool needCtr = true]) { - Widget introPanel() => Material( - color: Colors.transparent, - child: CustomScrollView( + Widget introPanel() => Scaffold( + backgroundColor: Colors.transparent, + body: CustomScrollView( key: const PageStorageKey('简介'), controller: needCtr ? _introController : null, physics: needCtr.not - ? const NeverScrollableScrollPhysics( + ? const AlwaysScrollableScrollPhysics( parent: ClampingScrollPhysics(), ) : null, @@ -2260,6 +2260,8 @@ class _VideoDetailPageVState extends State ], ), body: SingleChildScrollView( + controller: ScrollController(), + physics: const AlwaysScrollableScrollPhysics(), child: Column( children: [ ...List.generate( @@ -2358,7 +2360,9 @@ class _VideoDetailPageVState extends State } else { videoDetailController.childKey.currentState?.showBottomSheet( backgroundColor: Colors.transparent, - (context) => listSheetContent(), + (context) => ViewPointsPage( + child: listSheetContent(), + ), ); } } @@ -2391,3 +2395,35 @@ class _VideoDetailPageVState extends State ); } } + +class ViewPointsPage extends StatefulWidget { + const ViewPointsPage({super.key, required this.child}); + + final Widget child; + + @override + State createState() => _ViewPointsPageState(); +} + +class _ViewPointsPageState extends State { + bool _isInit = true; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + _isInit = false; + }); + }); + } + + @override + Widget build(BuildContext context) { + return _isInit + ? CustomScrollView( + physics: const NeverScrollableScrollPhysics(), + ) + : widget.child; + } +} diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 89169a721..3ef7df6cc 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -1125,6 +1125,81 @@ class _PLVideoPlayerState extends State child: Semantics( label: '双击开关控件', child: GestureDetector( + onVerticalDragStart: (details) { + if (plPlayerController.controlsLock.value) return; + if (details.localPosition.dy < 40) return; + _initialFocalPoint = details.localPosition; + _gestureType = null; + }, + onVerticalDragUpdate: (details) { + if (plPlayerController.controlsLock.value) return; + RenderBox renderBox = + _playerKey.currentContext!.findRenderObject() as RenderBox; + final double totalWidth = renderBox.size.width; + final double tapPosition = details.localPosition.dx; + final double sectionWidth = totalWidth / 3; + if (tapPosition < sectionWidth) { + // 左边区域 + _gestureType = 'left'; + } else if (tapPosition < sectionWidth * 2) { + // 全屏 + _gestureType = 'center'; + } else { + // 右边区域 + _gestureType = 'right'; + } + if (_gestureType == 'left') { + // 左边区域 👈 + final double level = renderBox.size.height * 3; + final double brightness = + _brightnessValue.value - details.delta.dy / level; + final double result = brightness.clamp(0.0, 1.0); + setBrightness(result); + } else if (_gestureType == 'center') { + // 全屏 + const double threshold = 2.5; // 滑动阈值 + double cumulativeDy = + details.localPosition.dy - _initialFocalPoint.dy; + + void fullScreenTrigger(bool status) { + EasyThrottle.throttle( + 'fullScreen', const Duration(milliseconds: 800), + () async { + await plPlayerController.triggerFullScreen( + status: status); + }); + } + + if (cumulativeDy > threshold) { + _gestureType = 'center_down'; + if (isFullScreen ^ fullScreenGestureReverse) { + fullScreenTrigger(fullScreenGestureReverse); + } + // debugPrint('center_down:$cumulativeDy'); + } else if (cumulativeDy < -threshold) { + _gestureType = 'center_up'; + if (!isFullScreen ^ fullScreenGestureReverse) { + fullScreenTrigger(!fullScreenGestureReverse); + } + // debugPrint('center_up:$cumulativeDy'); + } + } else if (_gestureType == 'right') { + // 右边区域 + final double level = renderBox.size.height * 0.5; + EasyThrottle.throttle( + 'setVolume', const Duration(milliseconds: 20), () { + final double volume = + _volumeValue.value - details.delta.dy / level; + final double result = volume.clamp(0.0, 1.0); + setVolume(result); + }); + } + }, + onVerticalDragEnd: (details) { + interacting = false; + _initialFocalPoint = Offset.zero; + _gestureType = null; + }, onTap: () { plPlayerController.controls = !plPlayerController.showControls.value;