From 20ff4cc9a4d6b7d0e63c4eef49acc33fed856f90 Mon Sep 17 00:00:00 2001 From: Ortes Date: Sun, 31 May 2026 15:48:52 -0500 Subject: [PATCH] feat: show click cursor on hover over Material controls and progress bar On web and desktop, the play/pause, mute, subtitles, fullscreen buttons and the seek/progress bar were built with `GestureDetector`, which does not change the mouse cursor on hover. As a result the pointer stayed the default arrow, giving no affordance that the controls are clickable. Wrap the clickable child of each of these `GestureDetector`s in a `MouseRegion(cursor: SystemMouseCursors.click)` so the cursor turns into a pointer on hover, matching the existing `IconButton`-based controls (options/subtitles) which already do this. The non-draggable progress bar is left untouched. Covers `MaterialControls`, `MaterialDesktopControls` and the shared `VideoProgressBar`. Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/src/material/material_controls.dart | 73 +++++++++++-------- .../material/material_desktop_controls.dart | 71 ++++++++++-------- lib/src/progress_bar.dart | 2 +- 3 files changed, 82 insertions(+), 64 deletions(-) diff --git a/lib/src/material/material_controls.dart b/lib/src/material/material_controls.dart index 3d43a1ab8..829662ac2 100644 --- a/lib/src/material/material_controls.dart +++ b/lib/src/material/material_controls.dart @@ -304,16 +304,19 @@ class _MaterialControlsState extends State controller.setVolume(0.0); } }, - child: AnimatedOpacity( - opacity: notifier.hideStuff ? 0.0 : 1.0, - duration: const Duration(milliseconds: 300), - child: ClipRect( - child: Container( - height: barHeight, - padding: const EdgeInsets.only(left: 6.0), - child: Icon( - _latestValue.volume > 0 ? Icons.volume_up : Icons.volume_off, - color: Colors.white, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: AnimatedOpacity( + opacity: notifier.hideStuff ? 0.0 : 1.0, + duration: const Duration(milliseconds: 300), + child: ClipRect( + child: Container( + height: barHeight, + padding: const EdgeInsets.only(left: 6.0), + child: Icon( + _latestValue.volume > 0 ? Icons.volume_up : Icons.volume_off, + color: Colors.white, + ), ), ), ), @@ -324,19 +327,22 @@ class _MaterialControlsState extends State GestureDetector _buildExpandButton() { return GestureDetector( onTap: _onExpandCollapse, - child: AnimatedOpacity( - opacity: notifier.hideStuff ? 0.0 : 1.0, - duration: const Duration(milliseconds: 300), - child: Container( - height: barHeight + (chewieController.isFullScreen ? 15.0 : 0), - margin: const EdgeInsets.only(right: 12.0), - padding: const EdgeInsets.only(left: 8.0, right: 8.0), - child: Center( - child: Icon( - chewieController.isFullScreen - ? Icons.fullscreen_exit - : Icons.fullscreen, - color: Colors.white, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: AnimatedOpacity( + opacity: notifier.hideStuff ? 0.0 : 1.0, + duration: const Duration(milliseconds: 300), + child: Container( + height: barHeight + (chewieController.isFullScreen ? 15.0 : 0), + margin: const EdgeInsets.only(right: 12.0), + padding: const EdgeInsets.only(left: 8.0, right: 8.0), + child: Center( + child: Icon( + chewieController.isFullScreen + ? Icons.fullscreen_exit + : Icons.fullscreen, + color: Colors.white, + ), ), ), ), @@ -473,15 +479,18 @@ class _MaterialControlsState extends State } return GestureDetector( onTap: _onSubtitleTap, - child: Container( - height: barHeight, - color: Colors.transparent, - padding: const EdgeInsets.only(left: 12.0, right: 12.0), - child: Icon( - _subtitleOn - ? Icons.closed_caption - : Icons.closed_caption_off_outlined, - color: _subtitleOn ? Colors.white : Colors.grey[700], + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: Container( + height: barHeight, + color: Colors.transparent, + padding: const EdgeInsets.only(left: 12.0, right: 12.0), + child: Icon( + _subtitleOn + ? Icons.closed_caption + : Icons.closed_caption_off_outlined, + color: _subtitleOn ? Colors.white : Colors.grey[700], + ), ), ), ); diff --git a/lib/src/material/material_desktop_controls.dart b/lib/src/material/material_desktop_controls.dart index acdd4a8b0..2503c5464 100644 --- a/lib/src/material/material_desktop_controls.dart +++ b/lib/src/material/material_desktop_controls.dart @@ -314,19 +314,22 @@ class _MaterialDesktopControlsState extends State GestureDetector _buildExpandButton() { return GestureDetector( onTap: _onExpandCollapse, - child: AnimatedOpacity( - opacity: notifier.hideStuff ? 0.0 : 1.0, - duration: const Duration(milliseconds: 300), - child: Container( - height: barHeight + (chewieController.isFullScreen ? 15.0 : 0), - margin: const EdgeInsets.only(right: 12.0), - padding: const EdgeInsets.only(left: 8.0, right: 8.0), - child: Center( - child: Icon( - chewieController.isFullScreen - ? Icons.fullscreen_exit - : Icons.fullscreen, - color: Colors.white, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: AnimatedOpacity( + opacity: notifier.hideStuff ? 0.0 : 1.0, + duration: const Duration(milliseconds: 300), + child: Container( + height: barHeight + (chewieController.isFullScreen ? 15.0 : 0), + margin: const EdgeInsets.only(right: 12.0), + padding: const EdgeInsets.only(left: 8.0, right: 8.0), + child: Center( + child: Icon( + chewieController.isFullScreen + ? Icons.fullscreen_exit + : Icons.fullscreen, + color: Colors.white, + ), ), ), ), @@ -409,16 +412,19 @@ class _MaterialDesktopControlsState extends State controller.setVolume(0.0); } }, - child: AnimatedOpacity( - opacity: notifier.hideStuff ? 0.0 : 1.0, - duration: const Duration(milliseconds: 300), - child: ClipRect( - child: Container( - height: barHeight, - padding: const EdgeInsets.only(right: 15.0), - child: Icon( - _latestValue.volume > 0 ? Icons.volume_up : Icons.volume_off, - color: Colors.white, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: AnimatedOpacity( + opacity: notifier.hideStuff ? 0.0 : 1.0, + duration: const Duration(milliseconds: 300), + child: ClipRect( + child: Container( + height: barHeight, + padding: const EdgeInsets.only(right: 15.0), + child: Icon( + _latestValue.volume > 0 ? Icons.volume_up : Icons.volume_off, + color: Colors.white, + ), ), ), ), @@ -429,14 +435,17 @@ class _MaterialDesktopControlsState extends State GestureDetector _buildPlayPause(VideoPlayerController controller) { return GestureDetector( onTap: _playPause, - child: Container( - height: barHeight, - color: Colors.transparent, - margin: const EdgeInsets.only(left: 8.0, right: 4.0), - padding: const EdgeInsets.only(left: 12.0, right: 12.0), - child: AnimatedPlayPause( - playing: controller.value.isPlaying, - color: Colors.white, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: Container( + height: barHeight, + color: Colors.transparent, + margin: const EdgeInsets.only(left: 8.0, right: 4.0), + padding: const EdgeInsets.only(left: 12.0, right: 12.0), + child: AnimatedPlayPause( + playing: controller.value.isPlaying, + color: Colors.white, + ), ), ), ); diff --git a/lib/src/progress_bar.dart b/lib/src/progress_bar.dart index 4b7eea470..4f2703935 100644 --- a/lib/src/progress_bar.dart +++ b/lib/src/progress_bar.dart @@ -117,7 +117,7 @@ class _VideoProgressBarState extends State { } _seekToRelativePosition(details.globalPosition); }, - child: child, + child: MouseRegion(cursor: SystemMouseCursors.click, child: child), ) : child; }