From a4e583fb388de327b38010e3ac50eb993cd138f2 Mon Sep 17 00:00:00 2001 From: Predidit <34627277+Predidit@users.noreply.github.com> Date: Fri, 22 Mar 2024 16:46:52 +0800 Subject: [PATCH] player redesign done --- lib/pages/player/player_controller.dart | 3 + lib/pages/video/video_page.dart | 616 +++++++++++++----------- lib/request/api.dart | 2 +- pubspec.yaml | 2 +- 4 files changed, 338 insertions(+), 285 deletions(-) diff --git a/lib/pages/player/player_controller.dart b/lib/pages/player/player_controller.dart index 55de30e..770a4ed 100644 --- a/lib/pages/player/player_controller.dart +++ b/lib/pages/player/player_controller.dart @@ -498,6 +498,9 @@ abstract class _PlayerController with Store { //退出全屏显示 Future exitFullScreen() async { debugPrint('退出全屏模式'); + if (Platform.isWindows) { + await windowManager.setFullScreen(false); + } dynamic document; late SystemUiMode mode = SystemUiMode.edgeToEdge; try { diff --git a/lib/pages/video/video_page.dart b/lib/pages/video/video_page.dart index 3e7d75d..c3aeadb 100644 --- a/lib/pages/video/video_page.dart +++ b/lib/pages/video/video_page.dart @@ -29,8 +29,23 @@ class _RatingPageState extends State with WindowListener { Timer? hideTimer; Timer? playerTimer; + @override + void initState() { + super.initState(); + if (playerController.bvid == '') { + videoController.init(playerController).then((_) { + playerTimer = getPlayerTimer(); + }); + } + } + @override void dispose() { + try { + playerTimer?.cancel(); + } catch (e) { + debugPrint(e.toString()); + } playerController.dispose(); super.dispose(); } @@ -114,9 +129,6 @@ class _RatingPageState extends State with WindowListener { double sheetHeight = MediaQuery.sizeOf(context).height - MediaQuery.of(context).padding.top - MediaQuery.sizeOf(context).width * 9 / 16; - if (playerController.bvid == '') { - videoController.init(playerController); - } return PopScope( // key: _key, canPop: false, @@ -128,296 +140,334 @@ class _RatingPageState extends State with WindowListener { body: Observer(builder: (context) { return Column( children: [ - playerController.dataStatus != 'loaded' ? SizedBox( - height: videoController.androidFullscreen - ? (MediaQuery.of(context).size.height) - : (MediaQuery.of(context).size.width * 9.0 / (16.0)), - width: MediaQuery.of(context).size.width, - child: const Center(child: CircularProgressIndicator()), - ) : Container( - color: Colors.black, - child: MouseRegion( - onHover: (_) { - _handleTap(); - }, - child: SizedBox( - height: videoController.androidFullscreen - ? (MediaQuery.of(context).size.height) - : (MediaQuery.of(context).size.width * 9.0 / (16.0)), - width: MediaQuery.of(context).size.width, - child: Stack(alignment: Alignment.center, children: [ - const Center(child: PlayerItem()), - videoController.isBuffering - ? const Positioned.fill( - child: Center( - child: CircularProgressIndicator(), - ), - ) - : Container(), - GestureDetector( - onTap: () async { - _handleTap; - try { - videoController.volume = - await FlutterVolumeController.getVolume() ?? - videoController.volume; - } catch (e) { - debugPrint(e.toString()); - } + playerController.dataStatus != 'loaded' + ? SizedBox( + height: videoController.androidFullscreen + ? (MediaQuery.of(context).size.height) + : (MediaQuery.of(context).size.width * + 9.0 / + (16.0)), + width: MediaQuery.of(context).size.width, + child: const Center(child: CircularProgressIndicator()), + ) + : Container( + color: Colors.black, + child: MouseRegion( + onHover: (_) { + _handleTap(); }, - child: Container( - color: Colors.transparent, - width: double.infinity, - height: double.infinity, - ), - ), + child: SizedBox( + height: videoController.androidFullscreen + ? (MediaQuery.of(context).size.height) + : (MediaQuery.of(context).size.width * + 9.0 / + (16.0)), + width: MediaQuery.of(context).size.width, + child: + Stack(alignment: Alignment.center, children: [ + const Center(child: PlayerItem()), + videoController.isBuffering + ? const Positioned.fill( + child: Center( + child: CircularProgressIndicator(), + ), + ) + : Container(), + GestureDetector( + onTap: () async { + _handleTap; + try { + videoController.volume = + await FlutterVolumeController + .getVolume() ?? + videoController.volume; + } catch (e) { + debugPrint(e.toString()); + } + }, + child: Container( + color: Colors.transparent, + width: double.infinity, + height: double.infinity, + ), + ), - // 播放器手势控制 - Positioned.fill( - left: 16, - top: 25, - right: 15, - bottom: 15, - child: GestureDetector(onHorizontalDragUpdate: - (DragUpdateDetails details) { - videoController.showPosition = true; - if (playerTimer != null) { - // debugPrint('检测到拖动, 定时器取消'); - playerTimer!.cancel(); - } - playerController.mediaPlayer.pause(); - final double scale = - 180000 / MediaQuery.sizeOf(context).width; - videoController.currentPosition = Duration( - milliseconds: videoController - .currentPosition.inMilliseconds + - (details.delta.dx * scale).round()); - }, onHorizontalDragEnd: (DragEndDetails details) { - playerController.mediaPlayer - .seek(videoController.currentPosition); - playerController.mediaPlayer.play(); - playerTimer = getPlayerTimer(); - videoController.showPosition = false; - }, onVerticalDragUpdate: - (DragUpdateDetails details) async { - final double totalWidth = - MediaQuery.sizeOf(context).width; - final double totalHeight = - MediaQuery.sizeOf(context).height; - final double tapPosition = - details.localPosition.dx; - final double sectionWidth = totalWidth / 2; - final double delta = details.delta.dy; + // 播放器手势控制 + Positioned.fill( + left: 16, + top: 25, + right: 15, + bottom: 15, + child: GestureDetector(onHorizontalDragUpdate: + (DragUpdateDetails details) { + videoController.showPosition = true; + if (playerTimer != null) { + // debugPrint('检测到拖动, 定时器取消'); + playerTimer!.cancel(); + } + playerController.mediaPlayer.pause(); + final double scale = 180000 / + MediaQuery.sizeOf(context).width; + videoController.currentPosition = Duration( + milliseconds: videoController + .currentPosition + .inMilliseconds + + (details.delta.dx * scale).round()); + }, onHorizontalDragEnd: + (DragEndDetails details) { + playerController.mediaPlayer + .seek(videoController.currentPosition); + playerController.mediaPlayer.play(); + playerTimer = getPlayerTimer(); + videoController.showPosition = false; + }, onVerticalDragUpdate: + (DragUpdateDetails details) async { + final double totalWidth = + MediaQuery.sizeOf(context).width; + final double totalHeight = + MediaQuery.sizeOf(context).height; + final double tapPosition = + details.localPosition.dx; + final double sectionWidth = totalWidth / 2; + final double delta = details.delta.dy; - /// 非全屏时禁用 - if (!videoController.androidFullscreen) { - return; - } - if (tapPosition < sectionWidth) { - // 左边区域 - videoController.showBrightness = true; - try { - videoController.brightness = - await ScreenBrightness().current; - } catch (e) { - debugPrint(e.toString()); - } - final double level = (totalHeight) * 3; - final double brightness = - videoController.brightness - delta / level; - final double result = - brightness.clamp(0.0, 1.0); - setBrightness(result); - } else { - // 右边区域 - videoController.showVolume = true; - final double level = (totalHeight) * 3; - final double volume = - videoController.volume - delta / level; - final double result = volume.clamp(0.0, 1.0); - setVolume(result); - videoController.volume = result; - } - }, onVerticalDragEnd: (DragEndDetails details) { - videoController.showBrightness = false; - videoController.showVolume = false; - })), - // 顶部进度条 - Positioned( - top: 25, - width: 200, - child: videoController.showPosition - ? Wrap( - alignment: WrapAlignment.center, - children: [ - Container( - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.5), - borderRadius: - BorderRadius.circular(8.0), // 圆角 - ), - child: Text( - '${videoController.currentPosition.inMinutes}:${(videoController.currentPosition.inSeconds) % 60}/${videoController.duration.inMinutes}:${(videoController.duration.inSeconds) % 60}', - style: const TextStyle( - color: Colors.white, - ), - ), - ), - ], - ) - : Container()), - // 亮度条 - Positioned( - top: 25, - child: videoController.showBrightness - ? Wrap( - alignment: WrapAlignment.center, - children: [ - Container( - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration( - color: - Colors.black.withOpacity(0.5), - borderRadius: BorderRadius.circular( - 8.0), // 圆角 - ), - child: Row( - children: [ - const Icon(Icons.brightness_7, - color: Colors.white), - Text( - ' ${(videoController.brightness * 100).toInt()} %', - style: const TextStyle( - color: Colors.white, - ), + /// 非全屏时禁用 + if (!videoController.androidFullscreen) { + return; + } + if (tapPosition < sectionWidth) { + // 左边区域 + videoController.showBrightness = true; + try { + videoController.brightness = + await ScreenBrightness().current; + } catch (e) { + debugPrint(e.toString()); + } + final double level = (totalHeight) * 3; + final double brightness = + videoController.brightness - + delta / level; + final double result = + brightness.clamp(0.0, 1.0); + setBrightness(result); + } else { + // 右边区域 + videoController.showVolume = true; + final double level = (totalHeight) * 3; + final double volume = + videoController.volume - + delta / level; + final double result = + volume.clamp(0.0, 1.0); + setVolume(result); + videoController.volume = result; + } + }, onVerticalDragEnd: + (DragEndDetails details) { + videoController.showBrightness = false; + videoController.showVolume = false; + })), + // 顶部进度条 + Positioned( + top: 25, + width: 200, + child: videoController.showPosition + ? Wrap( + alignment: WrapAlignment.center, + children: [ + Container( + padding: + const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.black + .withOpacity(0.5), + borderRadius: + BorderRadius.circular( + 8.0), // 圆角 ), - ], - )), - ], - ) - : Container()), - // 音量条 - Positioned( - top: 25, - child: videoController.showVolume - ? Wrap( - alignment: WrapAlignment.center, - children: [ - Container( - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration( - color: - Colors.black.withOpacity(0.5), - borderRadius: BorderRadius.circular( - 8.0), // 圆角 - ), - child: Row( - children: [ - const Icon(Icons.volume_down, - color: Colors.white), - Text( - ' ${(videoController.volume * 100).toInt()}%', + child: Text( + '${videoController.currentPosition.inMinutes}:${(videoController.currentPosition.inSeconds) % 60}/${videoController.duration.inMinutes}:${(videoController.duration.inSeconds) % 60}', style: const TextStyle( color: Colors.white, ), ), - ], - )), - ], - ) - : Container()), - (videoController.showPositioned || - !playerController.mediaPlayer.state.playing) - ? Positioned( - top: 0, - left: 0, - child: IconButton( - color: Colors.white, - icon: const Icon(Icons.arrow_back), - onPressed: () { - if (videoController.androidFullscreen == - true) { - playerController.exitFullScreen(); - videoController.androidFullscreen = false; - return; - } - navigationBarState.showNavigate(); - videoController.from == '/tab/popular/' - ? navigationBarState - .updateSelectedIndex(0) - : (videoController.from == - '/tab/follow/' - ? navigationBarState - .updateSelectedIndex(2) - : navigationBarState - .updateSelectedIndex(1)); - Modular.to.navigate(videoController.from); - }, - ), - ) - : Container(), - - // 自定义播放器底部组件 - (videoController.showPositioned || - !playerController.mediaPlayer.state.playing) - ? Positioned( - bottom: 0, - left: 0, - right: 0, - child: Row( - children: [ - IconButton( - color: Colors.white, - icon: Icon(videoController.playing - ? Icons.pause - : Icons.play_arrow), - onPressed: () { - if (videoController.playing) { - playerController.mediaPlayer.pause(); - } else { - playerController.mediaPlayer.play(); - } - }, - ), - Expanded( - child: ProgressBar( - timeLabelLocation: - TimeLabelLocation.none, - progress: - videoController.currentPosition, - buffered: videoController.buffer, - total: videoController.duration, - onSeek: (duration) { - playerController.mediaPlayer - .seek(duration); + ), + ], + ) + : Container()), + // 亮度条 + Positioned( + top: 25, + child: videoController.showBrightness + ? Wrap( + alignment: WrapAlignment.center, + children: [ + Container( + padding: + const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.black + .withOpacity(0.5), + borderRadius: + BorderRadius.circular( + 8.0), // 圆角 + ), + child: Row( + children: [ + const Icon( + Icons.brightness_7, + color: Colors.white), + Text( + ' ${(videoController.brightness * 100).toInt()} %', + style: const TextStyle( + color: Colors.white, + ), + ), + ], + )), + ], + ) + : Container()), + // 音量条 + Positioned( + top: 25, + child: videoController.showVolume + ? Wrap( + alignment: WrapAlignment.center, + children: [ + Container( + padding: + const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.black + .withOpacity(0.5), + borderRadius: + BorderRadius.circular( + 8.0), // 圆角 + ), + child: Row( + children: [ + const Icon( + Icons.volume_down, + color: Colors.white), + Text( + ' ${(videoController.volume * 100).toInt()}%', + style: const TextStyle( + color: Colors.white, + ), + ), + ], + )), + ], + ) + : Container()), + (videoController.showPositioned || + !playerController + .mediaPlayer.state.playing) + ? Positioned( + top: 0, + left: 0, + child: IconButton( + color: Colors.white, + icon: const Icon(Icons.arrow_back), + onPressed: () { + if (videoController + .androidFullscreen == + true) { + playerController.exitFullScreen(); + videoController.androidFullscreen = + false; + return; + } + navigationBarState.showNavigate(); + videoController.from == + '/tab/popular/' + ? navigationBarState + .updateSelectedIndex(0) + : (videoController.from == + '/tab/follow/' + ? navigationBarState + .updateSelectedIndex(2) + : navigationBarState + .updateSelectedIndex(1)); + Modular.to + .navigate(videoController.from); }, ), - ), - IconButton( - color: Colors.white, - icon: Icon( - videoController.androidFullscreen - ? Icons.fullscreen - : Icons.fullscreen_exit), - onPressed: () { - if (videoController.androidFullscreen) { - playerController.exitFullScreen(); - } else { - playerController.enterFullScreen(); - } - videoController.androidFullscreen = - !videoController.androidFullscreen; - }, - ), - ], - ), - ) - : Container(), - ]), - ), - ), - ), + ) + : Container(), + + // 自定义播放器底部组件 + (videoController.showPositioned || + !playerController + .mediaPlayer.state.playing) + ? Positioned( + bottom: 0, + left: 0, + right: 0, + child: Row( + children: [ + IconButton( + color: Colors.white, + icon: Icon(videoController.playing + ? Icons.pause + : Icons.play_arrow), + onPressed: () { + if (videoController.playing) { + playerController.mediaPlayer + .pause(); + } else { + playerController.mediaPlayer + .play(); + } + }, + ), + Expanded( + child: ProgressBar( + timeLabelLocation: + TimeLabelLocation.none, + progress: videoController + .currentPosition, + buffered: videoController.buffer, + total: videoController.duration, + onSeek: (duration) { + playerController.mediaPlayer + .seek(duration); + }, + ), + ), + IconButton( + color: Colors.white, + icon: Icon(videoController + .androidFullscreen + ? Icons.fullscreen + : Icons.fullscreen_exit), + onPressed: () { + if (videoController + .androidFullscreen) { + debugPrint('尝试退出全屏'); + playerController + .exitFullScreen(); + } else { + debugPrint('尝试进入全屏'); + playerController + .enterFullScreen(); + } + videoController + .androidFullscreen = + !videoController + .androidFullscreen; + }, + ), + ], + ), + ) + : Container(), + ]), + ), + ), + ), videoController.androidFullscreen ? Container() diff --git a/lib/request/api.dart b/lib/request/api.dart index fd10d66..c4c6b95 100644 --- a/lib/request/api.dart +++ b/lib/request/api.dart @@ -319,7 +319,7 @@ class Api { 'https://api.github.com/repos/Predidit/BiliNeo/releases/latest'; // 当前版本 - static const String version = '1.0.1'; + static const String version = '1.0.2'; static const sourceUrl = "https://github.com/Predidit/BiliNeo"; diff --git a/pubspec.yaml b/pubspec.yaml index 9a2f5df..75d4b58 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.1+1 +version: 1.0.2+1 environment: sdk: '>=3.2.6 <4.0.0'