From 74163598fbddfad5663dd1fc2a605e6b1913b705 Mon Sep 17 00:00:00 2001 From: Ortes Date: Sun, 15 Dec 2024 19:35:28 +0100 Subject: [PATCH 1/4] Add keyboard controls seek forward and backward and fullscreen escape --- .../material/material_desktop_controls.dart | 110 +++++++++++++----- 1 file changed, 79 insertions(+), 31 deletions(-) diff --git a/lib/src/material/material_desktop_controls.dart b/lib/src/material/material_desktop_controls.dart index 1b8fba8e2..6133b3290 100644 --- a/lib/src/material/material_desktop_controls.dart +++ b/lib/src/material/material_desktop_controls.dart @@ -12,6 +12,7 @@ import 'package:chewie/src/models/option_item.dart'; import 'package:chewie/src/models/subtitle_model.dart'; import 'package:chewie/src/notifiers/index.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:video_player/video_player.dart'; @@ -49,6 +50,7 @@ class _MaterialDesktopControlsState extends State late VideoPlayerController controller; ChewieController? _chewieController; + late final FocusNode _focusNode; // We know that _chewieController is set in didChangeDependencies ChewieController get chewieController => _chewieController!; @@ -75,39 +77,55 @@ class _MaterialDesktopControlsState extends State ); } - return MouseRegion( - onHover: (_) { - _cancelAndRestartTimer(); + return KeyboardListener( + focusNode: _focusNode, + onKeyEvent: (event) { + if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.space) { + _playPause(); + } else if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.arrowRight) { + _seekForward(); + } else if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.arrowLeft) { + _seekBackward(); + } else if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.escape) { + if (chewieController.isFullScreen) { + _onExpandCollapse(); + } + } }, - child: GestureDetector( - onTap: () => _cancelAndRestartTimer(), - child: AbsorbPointer( - absorbing: notifier.hideStuff, - child: Stack( - children: [ - if (_displayBufferingIndicator) - _chewieController?.bufferingBuilder?.call(context) ?? - const Center( - child: CircularProgressIndicator(), - ) - else - _buildHitArea(), - Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - if (_subtitleOn) - Transform.translate( - offset: Offset( - 0.0, - notifier.hideStuff ? barHeight * 0.8 : 0.0, + child: MouseRegion( + onHover: (_) { + _cancelAndRestartTimer(); + }, + child: GestureDetector( + onTap: () => _cancelAndRestartTimer(), + child: AbsorbPointer( + absorbing: notifier.hideStuff, + child: Stack( + children: [ + if (_displayBufferingIndicator) + _chewieController?.bufferingBuilder?.call(context) ?? + const Center( + child: CircularProgressIndicator(), + ) + else + _buildHitArea(), + Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (_subtitleOn) + Transform.translate( + offset: Offset( + 0.0, + notifier.hideStuff ? barHeight * 0.8 : 0.0, + ), + child: + _buildSubtitles(context, chewieController.subtitle!), ), - child: - _buildSubtitles(context, chewieController.subtitle!), - ), - _buildBottomBar(context), - ], - ), - ], + _buildBottomBar(context), + ], + ), + ], + ), ), ), ), @@ -565,6 +583,36 @@ class _MaterialDesktopControlsState extends State }); } + void _seekBackward() { + _seekRelative( + const Duration( + seconds: -10, + ), + ); + } + + void _seekForward() { + _seekRelative( + const Duration( + seconds: 10, + ), + ); + } + + void _seekRelative(Duration relativeSeek) { + _cancelAndRestartTimer(); + final position = _latestValue.position + relativeSeek; + final duration = _latestValue.duration; + + if (position < Duration.zero) { + controller.seekTo(Duration.zero); + } else if (position > duration) { + controller.seekTo(duration); + } else { + controller.seekTo(position); + } + } + Widget _buildProgressBar() { return Expanded( child: MaterialVideoProgressBar( From 8a40655598ffe483e7f930fdd67f5f52c20f1fb4 Mon Sep 17 00:00:00 2001 From: Ortes Date: Mon, 16 Dec 2024 16:19:09 +0100 Subject: [PATCH 2/4] Add init and dispose focuseNode keyboard controls --- lib/src/material/material_desktop_controls.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/material/material_desktop_controls.dart b/lib/src/material/material_desktop_controls.dart index 6133b3290..f612c8380 100644 --- a/lib/src/material/material_desktop_controls.dart +++ b/lib/src/material/material_desktop_controls.dart @@ -58,6 +58,7 @@ class _MaterialDesktopControlsState extends State @override void initState() { super.initState(); + _focusNode = FocusNode(); notifier = Provider.of(context, listen: false); } @@ -139,6 +140,7 @@ class _MaterialDesktopControlsState extends State } void _dispose() { + _focusNode.dispose(); controller.removeListener(_updateState); _hideTimer?.cancel(); _initTimer?.cancel(); From 3df5e00ef53259536268a1e0c48d4e4a98ccf7cf Mon Sep 17 00:00:00 2001 From: Ortes Date: Mon, 16 Dec 2024 16:58:43 +0100 Subject: [PATCH 3/4] Fix focusNode dispose issue --- .../material/material_desktop_controls.dart | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/lib/src/material/material_desktop_controls.dart b/lib/src/material/material_desktop_controls.dart index f612c8380..660fdcb6c 100644 --- a/lib/src/material/material_desktop_controls.dart +++ b/lib/src/material/material_desktop_controls.dart @@ -59,9 +59,27 @@ class _MaterialDesktopControlsState extends State void initState() { super.initState(); _focusNode = FocusNode(); + _focusNode.requestFocus(); notifier = Provider.of(context, listen: false); } + void _handleKeyPress(event) { + if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.space) { + _playPause(); + } else if (event is KeyDownEvent && + event.logicalKey == LogicalKeyboardKey.arrowRight) { + _seekForward(); + } else if (event is KeyDownEvent && + event.logicalKey == LogicalKeyboardKey.arrowLeft) { + _seekBackward(); + } else if (event is KeyDownEvent && + event.logicalKey == LogicalKeyboardKey.escape) { + if (chewieController.isFullScreen) { + _onExpandCollapse(); + } + } + } + @override Widget build(BuildContext context) { if (_latestValue.hasError) { @@ -78,23 +96,13 @@ class _MaterialDesktopControlsState extends State ); } + return KeyboardListener( focusNode: _focusNode, - onKeyEvent: (event) { - if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.space) { - _playPause(); - } else if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.arrowRight) { - _seekForward(); - } else if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.arrowLeft) { - _seekBackward(); - } else if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.escape) { - if (chewieController.isFullScreen) { - _onExpandCollapse(); - } - } - }, + onKeyEvent: _handleKeyPress, child: MouseRegion( onHover: (_) { + _focusNode.requestFocus(); _cancelAndRestartTimer(); }, child: GestureDetector( @@ -119,8 +127,8 @@ class _MaterialDesktopControlsState extends State 0.0, notifier.hideStuff ? barHeight * 0.8 : 0.0, ), - child: - _buildSubtitles(context, chewieController.subtitle!), + child: _buildSubtitles( + context, chewieController.subtitle!), ), _buildBottomBar(context), ], @@ -136,11 +144,11 @@ class _MaterialDesktopControlsState extends State @override void dispose() { _dispose(); + _focusNode.dispose(); super.dispose(); } void _dispose() { - _focusNode.dispose(); controller.removeListener(_updateState); _hideTimer?.cancel(); _initTimer?.cancel(); From 6cab13d2a2dee31c7fbbfe3806d0f885a59f90db Mon Sep 17 00:00:00 2001 From: Diego Tori Date: Mon, 6 Jan 2025 12:36:36 -0500 Subject: [PATCH 4/4] Formatted PR files. --- lib/src/material/material_desktop_controls.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/material/material_desktop_controls.dart b/lib/src/material/material_desktop_controls.dart index 784705d0e..3397cc2d1 100644 --- a/lib/src/material/material_desktop_controls.dart +++ b/lib/src/material/material_desktop_controls.dart @@ -96,7 +96,6 @@ class _MaterialDesktopControlsState extends State ); } - return KeyboardListener( focusNode: _focusNode, onKeyEvent: _handleKeyPress,