Skip to content

Commit

Permalink
Merge pull request fluttercommunity#712 from upvorg/dragable-progressbar
Browse files Browse the repository at this point in the history
Dragable progressbar
  • Loading branch information
diegotori authored May 12, 2023
2 parents 2c25e20 + de59314 commit a4815a2
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 64 deletions.
6 changes: 6 additions & 0 deletions lib/src/chewie_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ class ChewieController extends ChangeNotifier {
this.aspectRatio,
this.autoInitialize = false,
this.autoPlay = false,
this.draggableProgressBar = true,
this.startAt,
this.looping = false,
this.fullScreenByDefault = false,
Expand Down Expand Up @@ -297,6 +298,7 @@ class ChewieController extends ChangeNotifier {
double? aspectRatio,
bool? autoInitialize,
bool? autoPlay,
bool? draggableProgressBar,
Duration? startAt,
bool? looping,
bool? fullScreenByDefault,
Expand Down Expand Up @@ -338,6 +340,7 @@ class ChewieController extends ChangeNotifier {
)? routePageBuilder,
}) {
return ChewieController(
draggableProgressBar: draggableProgressBar ?? this.draggableProgressBar,
videoPlayerController:
videoPlayerController ?? this.videoPlayerController,
optionsTranslation: optionsTranslation ?? this.optionsTranslation,
Expand Down Expand Up @@ -430,6 +433,9 @@ class ChewieController extends ChangeNotifier {
/// Play the video as soon as it's displayed
final bool autoPlay;

/// Non-Draggable Progress Bar
final bool draggableProgressBar;

/// Start video at a certain position
final Duration? startAt;

Expand Down
3 changes: 3 additions & 0 deletions lib/src/cupertino/cupertino_controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,9 @@ class _CupertinoControlsState extends State<CupertinoControls>

_hideTimer?.cancel();
},
onDragUpdate: () {
_hideTimer?.cancel();
},
onDragEnd: () {
setState(() {
_dragging = false;
Expand Down
3 changes: 3 additions & 0 deletions lib/src/material/material_controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,9 @@ class _MaterialControlsState extends State<MaterialControls>

_hideTimer?.cancel();
},
onDragUpdate: () {
_hideTimer?.cancel();
},
onDragEnd: () {
setState(() {
_dragging = false;
Expand Down
3 changes: 3 additions & 0 deletions lib/src/material/material_desktop_controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,9 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>

_hideTimer?.cancel();
},
onDragUpdate: () {
_hideTimer?.cancel();
},
onDragEnd: () {
setState(() {
_dragging = false;
Expand Down
183 changes: 119 additions & 64 deletions lib/src/progress_bar.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import 'dart:io';

import 'package:chewie/src/chewie_progress_colors.dart';
import 'package:chewie/chewie.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';

Expand Down Expand Up @@ -43,6 +41,8 @@ class _VideoProgressBarState extends State<VideoProgressBar> {

bool _controllerWasPlaying = false;

Offset? _latestDraggableOffset;

VideoPlayerController get controller => widget.controller;

@override
Expand All @@ -58,70 +58,107 @@ class _VideoProgressBarState extends State<VideoProgressBar> {
}

void _seekToRelativePosition(Offset globalPosition) {
final box = context.findRenderObject()! as RenderBox;
final Offset tapPos = box.globalToLocal(globalPosition);
final double relative = tapPos.dx / box.size.width;
final Duration position = controller.value.duration * relative;
controller.seekTo(position);
controller.seekTo(context.calcRelativePosition(
controller.value.duration,
globalPosition,
));
}

@override
Widget build(BuildContext context) {
return GestureDetector(
onHorizontalDragStart: (DragStartDetails details) {
if (!controller.value.isInitialized) {
return;
}
_controllerWasPlaying = controller.value.isPlaying;
if (_controllerWasPlaying) {
controller.pause();
}

widget.onDragStart?.call();
},
onHorizontalDragUpdate: (DragUpdateDetails details) {
if (!controller.value.isInitialized) {
return;
}
// Should only seek if it's not running on Android, or if it is,
// then the VideoPlayerController cannot be buffering.
// On Android, we need to let the player buffer when scrolling
// in order to let the player buffer. https://github.com/flutter/flutter/issues/101409
final shouldSeekToRelativePosition =
!Platform.isAndroid || !controller.value.isBuffering;
if (shouldSeekToRelativePosition) {
_seekToRelativePosition(details.globalPosition);
}

widget.onDragUpdate?.call();
},
onHorizontalDragEnd: (DragEndDetails details) {
if (_controllerWasPlaying) {
controller.play();
}

widget.onDragEnd?.call();
},
onTapDown: (TapDownDetails details) {
if (!controller.value.isInitialized) {
return;
}
_seekToRelativePosition(details.globalPosition);
},
child: Center(
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: Colors.transparent,
child: CustomPaint(
painter: _ProgressBarPainter(
value: controller.value,
colors: widget.colors,
barHeight: widget.barHeight,
handleHeight: widget.handleHeight,
drawShadow: widget.drawShadow,
),
final ChewieController chewieController = ChewieController.of(context);
final child = Center(
child: StaticProgressBar(
value: controller.value,
colors: widget.colors,
barHeight: widget.barHeight,
handleHeight: widget.handleHeight,
drawShadow: widget.drawShadow,
),
);

return chewieController.draggableProgressBar
? GestureDetector(
onHorizontalDragStart: (DragStartDetails details) {
if (!controller.value.isInitialized) {
return;
}
_controllerWasPlaying = controller.value.isPlaying;
if (_controllerWasPlaying) {
controller.pause();
}

widget.onDragStart?.call();
},
onHorizontalDragUpdate: (DragUpdateDetails details) {
if (!controller.value.isInitialized) {
return;
}
_latestDraggableOffset = details.globalPosition;
listener();

widget.onDragUpdate?.call();
},
onHorizontalDragEnd: (DragEndDetails details) {
if (_controllerWasPlaying) {
controller.play();
}

if (_latestDraggableOffset != null) {
_seekToRelativePosition(_latestDraggableOffset!);
_latestDraggableOffset = null;
}

widget.onDragEnd?.call();
},
onTapDown: (TapDownDetails details) {
if (!controller.value.isInitialized) {
return;
}
_seekToRelativePosition(details.globalPosition);
},
child: child,
)
: child;
}
}

class StaticProgressBar extends StatelessWidget {
const StaticProgressBar({
Key? key,
required this.value,
required this.colors,
required this.barHeight,
required this.handleHeight,
required this.drawShadow,
this.latestDraggableOffset,
}) : super(key: key);

final Offset? latestDraggableOffset;
final VideoPlayerValue value;
final ChewieProgressColors colors;

final double barHeight;
final double handleHeight;
final bool drawShadow;

@override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: Colors.transparent,
child: CustomPaint(
painter: _ProgressBarPainter(
value: value,
draggableValue: context.calcRelativePosition(
value.duration,
latestDraggableOffset,
),
colors: colors,
barHeight: barHeight,
handleHeight: handleHeight,
drawShadow: drawShadow,
),
),
);
Expand All @@ -135,6 +172,7 @@ class _ProgressBarPainter extends CustomPainter {
required this.barHeight,
required this.handleHeight,
required this.drawShadow,
required this.draggableValue,
});

VideoPlayerValue value;
Expand All @@ -143,6 +181,7 @@ class _ProgressBarPainter extends CustomPainter {
final double barHeight;
final double handleHeight;
final bool drawShadow;
final Duration draggableValue;

@override
bool shouldRepaint(CustomPainter painter) {
Expand All @@ -166,8 +205,10 @@ class _ProgressBarPainter extends CustomPainter {
if (!value.isInitialized) {
return;
}
final double playedPartPercent =
value.position.inMilliseconds / value.duration.inMilliseconds;
final double playedPartPercent = (draggableValue != Duration.zero
? draggableValue.inMilliseconds
: value.position.inMilliseconds) /
value.duration.inMilliseconds;
final double playedPart =
playedPartPercent > 1 ? size.width : playedPartPercent * size.width;
for (final DurationRange range in value.buffered) {
Expand Down Expand Up @@ -214,3 +255,17 @@ class _ProgressBarPainter extends CustomPainter {
);
}
}

extension RelativePositionExtensions on BuildContext {
Duration calcRelativePosition(
Duration videoDuration,
Offset? globalPosition,
) {
if (globalPosition == null) return Duration.zero;
final box = findRenderObject()! as RenderBox;
final Offset tapPos = box.globalToLocal(globalPosition);
final double relative = tapPos.dx / box.size.width;
final Duration position = videoDuration * relative;
return position;
}
}

0 comments on commit a4815a2

Please sign in to comment.