diff --git a/lib/src/cupertino/cupertino_controls.dart b/lib/src/cupertino/cupertino_controls.dart index 7e16b38a1..39d770563 100644 --- a/lib/src/cupertino/cupertino_controls.dart +++ b/lib/src/cupertino/cupertino_controls.dart @@ -3,12 +3,12 @@ import 'dart:math' as math; import 'dart:ui' as ui; import 'package:chewie/src/animated_play_pause.dart'; -import 'package:chewie/src/center_play_button.dart'; import 'package:chewie/src/chewie_player.dart'; import 'package:chewie/src/chewie_progress_colors.dart'; import 'package:chewie/src/cupertino/cupertino_progress_bar.dart'; import 'package:chewie/src/cupertino/widgets/cupertino_options_dialog.dart'; import 'package:chewie/src/helpers/utils.dart'; +import 'package:chewie/src/hit_area_controls.dart'; import 'package:chewie/src/models/option_item.dart'; import 'package:chewie/src/models/subtitle_model.dart'; import 'package:chewie/src/notifiers/index.dart'; @@ -22,12 +22,14 @@ class CupertinoControls extends StatefulWidget { required this.backgroundColor, required this.iconColor, this.showPlayButton = true, + this.showSeekButton = true, Key? key, }) : super(key: key); final Color backgroundColor; final Color iconColor; final bool showPlayButton; + final bool showSeekButton; @override State createState() { @@ -350,8 +352,8 @@ class _CupertinoControlsState extends State final bool showPlayButton = widget.showPlayButton && !_latestValue.isPlaying && !_dragging; - return GestureDetector( - onTap: _latestValue.isPlaying + return HitAreaControls( + onTapPlay: _latestValue.isPlaying ? _cancelAndRestartTimer : () { _hideTimer?.cancel(); @@ -360,14 +362,13 @@ class _CupertinoControlsState extends State notifier.hideStuff = false; }); }, - child: CenterPlayButton( - backgroundColor: widget.backgroundColor, - iconColor: widget.iconColor, - isFinished: isFinished, - isPlaying: controller.value.isPlaying, - show: showPlayButton, - onPressed: _playPause, - ), + backgroundColor: widget.backgroundColor, + iconColor: widget.iconColor, + isFinished: isFinished, + isPlaying: controller.value.isPlaying, + showPlayButton: showPlayButton, + showSeekButton: false, + onPressedPlay: _playPause, ); } diff --git a/lib/src/hit_area_controls.dart b/lib/src/hit_area_controls.dart new file mode 100644 index 000000000..7e3deaf46 --- /dev/null +++ b/lib/src/hit_area_controls.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; + +import 'center_play_button.dart'; +import 'seek_control_button.dart'; + +class HitAreaControls extends StatelessWidget { + const HitAreaControls({ + Key? key, + required this.onTapPlay, + required this.onPressedPlay, + required this.backgroundColor, + required this.iconColor, + required this.isFinished, + required this.isPlaying, + required this.showPlayButton, + required this.showSeekButton, + this.seekRewind, + this.seekForward, + }) : super(key: key); + + final VoidCallback onTapPlay; + final VoidCallback onPressedPlay; + final VoidCallback? seekRewind; + final VoidCallback? seekForward; + final Color backgroundColor; + final Color iconColor; + final bool isFinished; + final bool isPlaying; + final bool showPlayButton; + final bool showSeekButton; + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + showSeekButton + ? SeekControlButton( + backgroundColor: backgroundColor, + iconColor: iconColor, + onPressed: seekRewind, + onDoublePressed: seekRewind, + icon: Icons.fast_rewind, + ) + : const SizedBox.shrink(), + GestureDetector( + onTap: onTapPlay, + child: CenterPlayButton( + backgroundColor: backgroundColor, + iconColor: iconColor, + isFinished: isFinished, + isPlaying: isPlaying, + show: showPlayButton, + onPressed: onPressedPlay, + ), + ), + showSeekButton + ? SeekControlButton( + backgroundColor: backgroundColor, + iconColor: iconColor, + onPressed: seekForward, + onDoublePressed: seekForward, + icon: Icons.fast_forward, + ) + : const SizedBox.shrink(), + ], + ); + } +} diff --git a/lib/src/material/material_controls.dart b/lib/src/material/material_controls.dart index 92be36fbe..0729130e6 100644 --- a/lib/src/material/material_controls.dart +++ b/lib/src/material/material_controls.dart @@ -1,9 +1,9 @@ import 'dart:async'; -import 'package:chewie/src/center_play_button.dart'; import 'package:chewie/src/chewie_player.dart'; import 'package:chewie/src/chewie_progress_colors.dart'; import 'package:chewie/src/helpers/utils.dart'; +import 'package:chewie/src/hit_area_controls.dart'; import 'package:chewie/src/material/material_progress_bar.dart'; import 'package:chewie/src/material/widgets/options_dialog.dart'; import 'package:chewie/src/material/widgets/playback_speed_dialog.dart'; @@ -17,10 +17,12 @@ import 'package:video_player/video_player.dart'; class MaterialControls extends StatefulWidget { const MaterialControls({ this.showPlayButton = true, + this.showSeekButton = true, Key? key, }) : super(key: key); final bool showPlayButton; + final bool showSeekButton; @override State createState() { @@ -366,9 +368,11 @@ class _MaterialControlsState extends State final bool isFinished = _latestValue.position >= _latestValue.duration; final bool showPlayButton = widget.showPlayButton && !_dragging && !notifier.hideStuff; + final bool showSeekButton = + widget.showSeekButton && !_dragging && !notifier.hideStuff; - return GestureDetector( - onTap: () { + return HitAreaControls( + onTapPlay: () { if (_latestValue.isPlaying) { if (_displayTapped) { setState(() { @@ -385,14 +389,15 @@ class _MaterialControlsState extends State }); } }, - child: CenterPlayButton( - backgroundColor: Colors.black54, - iconColor: Colors.white, - isFinished: isFinished, - isPlaying: controller.value.isPlaying, - show: showPlayButton, - onPressed: _playPause, - ), + backgroundColor: Colors.black54, + iconColor: Colors.white, + isFinished: isFinished, + isPlaying: controller.value.isPlaying, + showPlayButton: showPlayButton, + showSeekButton: showSeekButton, + onPressedPlay: _playPause, + seekRewind: _seekRewind, + seekForward: _seekForward, ); } @@ -542,6 +547,20 @@ class _MaterialControlsState extends State }); } + void _seekTo({int seconds = 10}) { + setState(() { + controller.seekTo( + Duration( + seconds: _latestValue.position.inSeconds + seconds, + ), + ); + }); + } + + void _seekForward() => _seekTo(); + + void _seekRewind() => _seekTo(seconds: -10); + void _startHideTimer() { final hideControlsTimer = chewieController.hideControlsTimer.isNegative ? ChewieController.defaultHideControlsTimer diff --git a/lib/src/material/material_desktop_controls.dart b/lib/src/material/material_desktop_controls.dart index 404031653..ea4b1e80d 100644 --- a/lib/src/material/material_desktop_controls.dart +++ b/lib/src/material/material_desktop_controls.dart @@ -1,10 +1,10 @@ import 'dart:async'; import 'package:chewie/src/animated_play_pause.dart'; -import 'package:chewie/src/center_play_button.dart'; import 'package:chewie/src/chewie_player.dart'; import 'package:chewie/src/chewie_progress_colors.dart'; import 'package:chewie/src/helpers/utils.dart'; +import 'package:chewie/src/hit_area_controls.dart'; import 'package:chewie/src/material/material_progress_bar.dart'; import 'package:chewie/src/material/widgets/options_dialog.dart'; import 'package:chewie/src/material/widgets/playback_speed_dialog.dart'; @@ -18,10 +18,12 @@ import 'package:video_player/video_player.dart'; class MaterialDesktopControls extends StatefulWidget { const MaterialDesktopControls({ this.showPlayButton = true, + this.showSeekButton = true, Key? key, }) : super(key: key); final bool showPlayButton; + final bool showSeekButton; @override State createState() { @@ -330,9 +332,11 @@ class _MaterialDesktopControlsState extends State final bool isFinished = _latestValue.position >= _latestValue.duration; final bool showPlayButton = widget.showPlayButton && !_dragging && !notifier.hideStuff; + final bool showSeekButton = + widget.showSeekButton && !_dragging && !notifier.hideStuff; - return GestureDetector( - onTap: () { + return HitAreaControls( + onTapPlay: () { if (_latestValue.isPlaying) { if (_displayTapped) { setState(() { @@ -349,14 +353,15 @@ class _MaterialDesktopControlsState extends State }); } }, - child: CenterPlayButton( - backgroundColor: Colors.black54, - iconColor: Colors.white, - isFinished: isFinished, - isPlaying: controller.value.isPlaying, - show: showPlayButton, - onPressed: _playPause, - ), + backgroundColor: Colors.black54, + iconColor: Colors.white, + isFinished: isFinished, + isPlaying: controller.value.isPlaying, + showPlayButton: showPlayButton, + showSeekButton: showSeekButton, + onPressedPlay: _playPause, + seekRewind: _seekRewind, + seekForward: _seekForward, ); } @@ -596,4 +601,18 @@ class _MaterialDesktopControlsState extends State ), ); } + + void _seekTo({int seconds = 10}) { + setState(() { + controller.seekTo( + Duration( + seconds: _latestValue.position.inSeconds + seconds, + ), + ); + }); + } + + void _seekForward() => _seekTo(); + + void _seekRewind() => _seekTo(seconds: -10); } diff --git a/lib/src/seek_control_button.dart b/lib/src/seek_control_button.dart new file mode 100644 index 000000000..667cb6964 --- /dev/null +++ b/lib/src/seek_control_button.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; + +class SeekControlButton extends StatefulWidget { + const SeekControlButton({ + Key? key, + required this.backgroundColor, + required this.iconColor, + required this.icon, + this.onPressed, + this.onDoublePressed, + }) : super(key: key); + + final Color backgroundColor; + final Color iconColor; + final VoidCallback? onPressed; + final VoidCallback? onDoublePressed; + final IconData icon; + + @override + State createState() => _SeekControlButtonState(); +} + +class _SeekControlButtonState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Expanded( + child: GestureDetector( + onDoubleTap: widget.onDoublePressed, + child: ColoredBox( + color: Colors.transparent, + child: Center( + child: DecoratedBox( + decoration: BoxDecoration( + color: widget.backgroundColor, + shape: BoxShape.circle, + ), + // Always set the iconSize on the IconButton, not on the Icon itself: + // https://github.com/flutter/flutter/issues/52980 + child: IconButton( + iconSize: 32, + padding: const EdgeInsets.all(12.0), + icon: Icon(widget.icon, color: widget.iconColor), + onPressed: widget.onPressed, + ), + ), + ), + ), + ), + ); + } +}