Skip to content

Commit

Permalink
Merge pull request #49 from thesensibledev/fix_horizontal_drag
Browse files Browse the repository at this point in the history
Fix horizontal drag and tap when ancestors have competing gestures.
  • Loading branch information
suragch authored Oct 1, 2022
2 parents 31c9051 + c5aa5bd commit 08527a5
Show file tree
Hide file tree
Showing 2 changed files with 439 additions and 73 deletions.
111 changes: 98 additions & 13 deletions lib/audio_video_progress_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ class ProgressBar extends LeafRenderObjectWidget {
this.onDragStart,
this.onDragUpdate,
this.onDragEnd,
this.onTap,
this.seekOnTap = true,
this.barHeight = 5.0,
this.baseBarColor,
this.progressBarColor,
Expand Down Expand Up @@ -123,6 +125,10 @@ class ProgressBar extends LeafRenderObjectWidget {
/// If you want continuous duration updates as the user moves the thumb,
/// see [onDragUpdate], where the provided [ThumbDragDetails] has a
/// `timeStamp` with the seek duration on it.
///
/// By default, this callback will also be invoked for tap events, with the
/// duration set based on the tap location. If this behavior of seeking on tap
/// is not desired, set the [seekOnTap] toggle to false.
final ValueChanged<Duration>? onSeek;

/// A callback when the user starts to move the thumb.
Expand Down Expand Up @@ -166,6 +172,34 @@ class ProgressBar extends LeafRenderObjectWidget {
/// This method is called directly before [onSeek].
final VoidCallback? onDragEnd;

/// A callback when the user finishes tapping on the progress bar.
///
/// This is called only once when the tap gesture is considered complete.
/// (E.g. When the user tapped without dragging or the user clicked with the
/// primary pointing device, such as a mouse.)
///
/// The behavior of this callback is not impacted by [seekOnTap].
/// But if [seekOnTap] is set to true, the [onTap] will be invoked before
/// [onSeek]
final VoidCallback? onTap;

/// Whether a tap on the progress bar will cause the tracking thumb to move
/// to the location of the tap and cause the onSeek to be invoked.
///
/// The default is `true` and this means that a tap will cause the thumb to
/// move and invoke an onSeek when the tap gesture is completed.
///
/// This doesn't impact the drag behavior. Therefore, if the user taps and
/// then drags, the tracking thumb will follow the drag and invoke the drag
/// callbacks.
///
/// It is useful to set this to 'false', if accidental taping on the progress
/// bar should not result in invoking a seeking / dragging behavior. It is
/// also useful to implement alternative interactions such as displaying a
/// more detailed progress bar, on tap and resetting it to a collapsed
/// or mini version by default.
final bool seekOnTap;

/// The vertical thickness of the progress bar.
final double barHeight;

Expand Down Expand Up @@ -265,6 +299,8 @@ class ProgressBar extends LeafRenderObjectWidget {
onDragStart: onDragStart,
onDragUpdate: onDragUpdate,
onDragEnd: onDragEnd,
onTap: onTap,
seekOnTap: seekOnTap,
barHeight: barHeight,
baseBarColor: baseBarColor ?? primaryColor.withOpacity(0.24),
progressBarColor: progressBarColor ?? primaryColor,
Expand Down Expand Up @@ -298,6 +334,8 @@ class ProgressBar extends LeafRenderObjectWidget {
..onDragStart = onDragStart
..onDragUpdate = onDragUpdate
..onDragEnd = onDragEnd
..onTap = onTap
..seekOnTap = seekOnTap
..barHeight = barHeight
..baseBarColor = baseBarColor ?? primaryColor.withOpacity(0.24)
..progressBarColor = progressBarColor ?? primaryColor
Expand Down Expand Up @@ -332,6 +370,17 @@ class ProgressBar extends LeafRenderObjectWidget {
ifNull: 'unimplemented'));
properties.add(ObjectFlagProperty<VoidCallback>('onDragEnd', onDragEnd,
ifNull: 'unimplemented'));
properties.add(ObjectFlagProperty<VoidCallback>('onTap', onTap,
ifNull: 'unimplemented'));
properties.add(
FlagProperty(
'seekOnTap',
value: seekOnTap,
ifTrue: 'will call onSeek when tapped',
ifFalse: "won't call onSeek when tapped",
showName: true,
),
);
properties.add(DoubleProperty('barHeight', barHeight));
properties.add(ColorProperty('baseBarColor', baseBarColor));
properties.add(ColorProperty('progressBarColor', progressBarColor));
Expand All @@ -341,8 +390,15 @@ class ProgressBar extends LeafRenderObjectWidget {
properties.add(ColorProperty('thumbColor', thumbColor));
properties.add(ColorProperty('thumbGlowColor', thumbGlowColor));
properties.add(DoubleProperty('thumbGlowRadius', thumbGlowRadius));
properties.add(FlagProperty('thumbCanPaintOutsideBar',
value: thumbCanPaintOutsideBar));
properties.add(
FlagProperty(
'thumbCanPaintOutsideBar',
value: thumbCanPaintOutsideBar,
ifTrue: 'true',
ifFalse: 'false',
showName: true,
),
);
properties
.add(StringProperty('timeLabelLocation', timeLabelLocation.toString()));
properties.add(StringProperty('timeLabelType', timeLabelType.toString()));
Expand Down Expand Up @@ -392,6 +448,8 @@ class _RenderProgressBar extends RenderBox {
ThumbDragStartCallback? onDragStart,
ThumbDragUpdateCallback? onDragUpdate,
VoidCallback? onDragEnd,
VoidCallback? onTap,
required bool seekOnTap,
required double barHeight,
required Color baseBarColor,
required Color progressBarColor,
Expand All @@ -414,6 +472,8 @@ class _RenderProgressBar extends RenderBox {
_onDragStartUserCallback = onDragStart,
_onDragUpdateUserCallback = onDragUpdate,
_onDragEndUserCallback = onDragEnd,
_onTapUserCallback = onTap,
_seekOnTap = seekOnTap,
_barHeight = barHeight,
_baseBarColor = baseBarColor,
_progressBarColor = progressBarColor,
Expand All @@ -429,18 +489,23 @@ class _RenderProgressBar extends RenderBox {
_timeLabelTextStyle = timeLabelTextStyle,
_timeLabelPadding = timeLabelPadding {
_drag = HorizontalDragGestureRecognizer()
..onDown = _onDragDown
..onStart = _onDragStart
..onUpdate = _onDragUpdate
..onEnd = _onDragEnd
..onCancel = _finishDrag
..gestureSettings = gestureSettings;
_tap = TapGestureRecognizer()
..onTapUp = _onTapUp
..gestureSettings = gestureSettings;
_thumbValue = _proportionOfTotal(_progress);
}

// This is the gesture recognizer used to move the thumb.
late HorizontalDragGestureRecognizer _drag;

// This is the gesture recognizer used to detect taps on the bar.
late TapGestureRecognizer _tap;

// This is a value between 0.0 and 1.0 used to indicate the position on
// the bar.
late double _thumbValue;
Expand All @@ -459,16 +524,6 @@ class _RenderProgressBar extends RenderBox {
return (_thumbCanPaintOutsideBar) ? thumbRadius + minPadding : minPadding;
}

// The Down event is the first event that is triggered. If null, the gesture
// will lose to other gesture recognizers that have handled tap / down.
// Also, may be the only event that will be received, if the user tapped,
// instead of dragging.
void _onDragDown(DragDownDetails details) {
_updateThumbPosition(details.localPosition);
onSeek?.call(_currentThumbDuration());
markNeedsPaint();
}

void _onDragStart(DragStartDetails details) {
_userIsDraggingThumb = true;
_updateThumbPosition(details.localPosition);
Expand Down Expand Up @@ -499,6 +554,17 @@ class _RenderProgressBar extends RenderBox {
markNeedsPaint();
}

// Call onTap first and then check if the seekOnTap is requested.
// If 'true' then update the thumb position and invoke the onSeek callback.
void _onTapUp(TapUpDetails details) {
onTap?.call();
if (_seekOnTap) {
_updateThumbPosition(details.localPosition);
onSeek?.call(_currentThumbDuration());
markNeedsPaint();
}
}

Duration _currentThumbDuration() {
final thumbMiliseconds = _thumbValue * total.inMilliseconds;
return Duration(milliseconds: thumbMiliseconds.round());
Expand Down Expand Up @@ -660,6 +726,24 @@ class _RenderProgressBar extends RenderBox {
_onDragEndUserCallback = value;
}

/// A callback when the bar is tapped
VoidCallback? get onTap => _onTapUserCallback;
VoidCallback? _onTapUserCallback;
set onTap(VoidCallback? value) {
if (value == _onTapUserCallback) {
return;
}
_onTapUserCallback = value;
}

/// A toggle to enable the onSeek callback on tap along with thumb tracking.
bool get seekOnTap => _seekOnTap;
bool _seekOnTap;
set seekOnTap(bool value) {
if (_seekOnTap == value) return;
_seekOnTap = value;
}

/// The vertical thickness of the bar that the thumb moves along.
double get barHeight => _barHeight;
double _barHeight;
Expand Down Expand Up @@ -819,6 +903,7 @@ class _RenderProgressBar extends RenderBox {
assert(debugHandleEvent(event, entry));
if (event is PointerDownEvent) {
_drag.addPointer(event);
_tap.addPointer(event);
}
}

Expand Down
Loading

0 comments on commit 08527a5

Please sign in to comment.