Clean up ScrollbarPainter (#107179)
This commit is contained in:
parent
c4aea1c3f8
commit
b972f872b8
@ -283,6 +283,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
|
|||||||
_shape = value;
|
_shape = value;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The amount of space by which to inset the scrollbar's start and end, as
|
/// The amount of space by which to inset the scrollbar's start and end, as
|
||||||
/// well as its side to the nearest edge, in logical pixels.
|
/// well as its side to the nearest edge, in logical pixels.
|
||||||
///
|
///
|
||||||
@ -304,7 +305,6 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// The preferred smallest size the scrollbar thumb can shrink to when the total
|
/// The preferred smallest size the scrollbar thumb can shrink to when the total
|
||||||
/// scrollable extent is large, the current visible viewport is small, and the
|
/// scrollable extent is large, the current visible viewport is small, and the
|
||||||
/// viewport is not overscrolled.
|
/// viewport is not overscrolled.
|
||||||
@ -391,23 +391,129 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - Scrollbar Details
|
||||||
|
|
||||||
|
Rect? _trackRect;
|
||||||
|
// The full painted length of the track
|
||||||
|
double get _trackExtent => _lastMetrics!.viewportDimension - _totalTrackMainAxisOffsets;
|
||||||
|
// The full length of the track that the thumb can travel
|
||||||
|
double get _traversableTrackExtent => _trackExtent - (2 * mainAxisMargin);
|
||||||
|
// Track Offsets
|
||||||
|
// The track is offset by only padding.
|
||||||
|
double get _totalTrackMainAxisOffsets => _isVertical ? padding.vertical : padding.horizontal;
|
||||||
|
double get _leadingTrackMainAxisOffset {
|
||||||
|
switch(_resolvedOrientation) {
|
||||||
|
case ScrollbarOrientation.left:
|
||||||
|
case ScrollbarOrientation.right:
|
||||||
|
return padding.top;
|
||||||
|
case ScrollbarOrientation.top:
|
||||||
|
case ScrollbarOrientation.bottom:
|
||||||
|
return padding.left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect? _thumbRect;
|
||||||
|
// The current scroll position + _leadingThumbMainAxisOffset
|
||||||
|
late double _thumbOffset;
|
||||||
|
// The fraction visible in relation to the trversable length of the track.
|
||||||
|
late double _thumbExtent;
|
||||||
|
// Thumb Offsets
|
||||||
|
// The thumb is offset by padding and margins.
|
||||||
|
double get _leadingThumbMainAxisOffset {
|
||||||
|
switch(_resolvedOrientation) {
|
||||||
|
case ScrollbarOrientation.left:
|
||||||
|
case ScrollbarOrientation.right:
|
||||||
|
return padding.top + mainAxisMargin;
|
||||||
|
case ScrollbarOrientation.top:
|
||||||
|
case ScrollbarOrientation.bottom:
|
||||||
|
return padding.left + mainAxisMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void _setThumbExtent() {
|
||||||
|
// Thumb extent reflects fraction of content visible, as long as this
|
||||||
|
// isn't less than the absolute minimum size.
|
||||||
|
// _totalContentExtent >= viewportDimension, so (_totalContentExtent - _mainAxisPadding) > 0
|
||||||
|
final double fractionVisible = clampDouble(
|
||||||
|
(_lastMetrics!.extentInside - _totalTrackMainAxisOffsets)
|
||||||
|
/ (_totalContentExtent - _totalTrackMainAxisOffsets),
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
final double thumbExtent = math.max(
|
||||||
|
math.min(_traversableTrackExtent, minOverscrollLength),
|
||||||
|
_traversableTrackExtent * fractionVisible,
|
||||||
|
);
|
||||||
|
|
||||||
|
final double fractionOverscrolled = 1.0 - _lastMetrics!.extentInside / _lastMetrics!.viewportDimension;
|
||||||
|
final double safeMinLength = math.min(minLength, _traversableTrackExtent);
|
||||||
|
final double newMinLength = (_beforeExtent > 0 && _afterExtent > 0)
|
||||||
|
// Thumb extent is no smaller than minLength if scrolling normally.
|
||||||
|
? safeMinLength
|
||||||
|
// User is overscrolling. Thumb extent can be less than minLength
|
||||||
|
// but no smaller than minOverscrollLength. We can't use the
|
||||||
|
// fractionVisible to produce intermediate values between minLength and
|
||||||
|
// minOverscrollLength when the user is transitioning from regular
|
||||||
|
// scrolling to overscrolling, so we instead use the percentage of the
|
||||||
|
// content that is still in the viewport to determine the size of the
|
||||||
|
// thumb. iOS behavior appears to have the thumb reach its minimum size
|
||||||
|
// with ~20% of overscroll. We map the percentage of minLength from
|
||||||
|
// [0.8, 1.0] to [0.0, 1.0], so 0% to 20% of overscroll will produce
|
||||||
|
// values for the thumb that range between minLength and the smallest
|
||||||
|
// possible value, minOverscrollLength.
|
||||||
|
: safeMinLength * (1.0 - clampDouble(fractionOverscrolled, 0.0, 0.2) / 0.2);
|
||||||
|
|
||||||
|
// The `thumbExtent` should be no greater than `trackSize`, otherwise
|
||||||
|
// the scrollbar may scroll towards the wrong direction.
|
||||||
|
_thumbExtent = clampDouble(thumbExtent, newMinLength, _traversableTrackExtent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Scrollable Details
|
||||||
|
|
||||||
|
ScrollMetrics? _lastMetrics;
|
||||||
|
bool get _lastMetricsAreScrollable => _lastMetrics!.minScrollExtent != _lastMetrics!.maxScrollExtent;
|
||||||
|
AxisDirection? _lastAxisDirection;
|
||||||
|
|
||||||
|
bool get _isVertical => _lastAxisDirection == AxisDirection.down || _lastAxisDirection == AxisDirection.up;
|
||||||
|
bool get _isReversed => _lastAxisDirection == AxisDirection.up || _lastAxisDirection == AxisDirection.left;
|
||||||
|
// The amount of scroll distance before and after the current position.
|
||||||
|
double get _beforeExtent => _isReversed ? _lastMetrics!.extentAfter : _lastMetrics!.extentBefore;
|
||||||
|
double get _afterExtent => _isReversed ? _lastMetrics!.extentBefore : _lastMetrics!.extentAfter;
|
||||||
|
|
||||||
|
// The total size of the scrollable content.
|
||||||
|
double get _totalContentExtent {
|
||||||
|
return _lastMetrics!.maxScrollExtent
|
||||||
|
- _lastMetrics!.minScrollExtent
|
||||||
|
+ _lastMetrics!.viewportDimension;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollbarOrientation get _resolvedOrientation {
|
||||||
|
if (scrollbarOrientation == null) {
|
||||||
|
if (_isVertical) {
|
||||||
|
return textDirection == TextDirection.ltr
|
||||||
|
? ScrollbarOrientation.right
|
||||||
|
: ScrollbarOrientation.left;
|
||||||
|
}
|
||||||
|
return ScrollbarOrientation.bottom;
|
||||||
|
}
|
||||||
|
return scrollbarOrientation!;
|
||||||
|
}
|
||||||
|
|
||||||
void _debugAssertIsValidOrientation(ScrollbarOrientation orientation) {
|
void _debugAssertIsValidOrientation(ScrollbarOrientation orientation) {
|
||||||
assert(
|
assert(
|
||||||
(_isVertical && _isVerticalOrientation(orientation)) || (!_isVertical && !_isVerticalOrientation(orientation)),
|
() {
|
||||||
'The given ScrollbarOrientation: $orientation is incompatible with the current AxisDirection: $_lastAxisDirection.'
|
bool isVerticalOrientation(ScrollbarOrientation orientation) =>
|
||||||
|
orientation == ScrollbarOrientation.left
|
||||||
|
|| orientation == ScrollbarOrientation.right;
|
||||||
|
return (_isVertical && isVerticalOrientation(orientation))
|
||||||
|
|| (!_isVertical && !isVerticalOrientation(orientation));
|
||||||
|
}(),
|
||||||
|
'The given ScrollbarOrientation: $orientation is incompatible with the '
|
||||||
|
'current AxisDirection: $_lastAxisDirection.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether given scrollbar orientation is vertical
|
// - Updating
|
||||||
bool _isVerticalOrientation(ScrollbarOrientation orientation) =>
|
|
||||||
orientation == ScrollbarOrientation.left
|
|
||||||
|| orientation == ScrollbarOrientation.right;
|
|
||||||
|
|
||||||
ScrollMetrics? _lastMetrics;
|
|
||||||
AxisDirection? _lastAxisDirection;
|
|
||||||
Rect? _thumbRect;
|
|
||||||
Rect? _trackRect;
|
|
||||||
late double _thumbOffset;
|
|
||||||
|
|
||||||
/// Update with new [ScrollMetrics]. If the metrics change, the scrollbar will
|
/// Update with new [ScrollMetrics]. If the metrics change, the scrollbar will
|
||||||
/// show and redraw itself based on these new metrics.
|
/// show and redraw itself based on these new metrics.
|
||||||
@ -433,7 +539,6 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
|
|||||||
if (!needPaint(oldMetrics) && !needPaint(metrics)) {
|
if (!needPaint(oldMetrics) && !needPaint(metrics)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,6 +548,8 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
|
|||||||
radius = nextRadius;
|
radius = nextRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - Painting
|
||||||
|
|
||||||
Paint get _paintThumb {
|
Paint get _paintThumb {
|
||||||
return Paint()
|
return Paint()
|
||||||
..color = color.withOpacity(color.opacity * fadeoutOpacityAnimation.value);
|
..color = color.withOpacity(color.opacity * fadeoutOpacityAnimation.value);
|
||||||
@ -459,67 +566,50 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
|
|||||||
..color = trackColor.withOpacity(trackColor.opacity * fadeoutOpacityAnimation.value);
|
..color = trackColor.withOpacity(trackColor.opacity * fadeoutOpacityAnimation.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _paintScrollbar(Canvas canvas, Size size, double thumbExtent, AxisDirection direction) {
|
void _paintScrollbar(Canvas canvas, Size size) {
|
||||||
assert(
|
assert(
|
||||||
textDirection != null,
|
textDirection != null,
|
||||||
'A TextDirection must be provided before a Scrollbar can be painted.',
|
'A TextDirection must be provided before a Scrollbar can be painted.',
|
||||||
);
|
);
|
||||||
|
|
||||||
final ScrollbarOrientation resolvedOrientation;
|
|
||||||
|
|
||||||
if (scrollbarOrientation == null) {
|
|
||||||
if (_isVertical) {
|
|
||||||
resolvedOrientation = textDirection == TextDirection.ltr
|
|
||||||
? ScrollbarOrientation.right
|
|
||||||
: ScrollbarOrientation.left;
|
|
||||||
} else {
|
|
||||||
resolvedOrientation = ScrollbarOrientation.bottom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
resolvedOrientation = scrollbarOrientation!;
|
|
||||||
}
|
|
||||||
|
|
||||||
final double x, y;
|
final double x, y;
|
||||||
final Size thumbSize, trackSize;
|
final Size thumbSize, trackSize;
|
||||||
final Offset trackOffset, borderStart, borderEnd;
|
final Offset trackOffset, borderStart, borderEnd;
|
||||||
|
_debugAssertIsValidOrientation(_resolvedOrientation);
|
||||||
_debugAssertIsValidOrientation(resolvedOrientation);
|
switch(_resolvedOrientation) {
|
||||||
|
|
||||||
switch(resolvedOrientation) {
|
|
||||||
case ScrollbarOrientation.left:
|
case ScrollbarOrientation.left:
|
||||||
thumbSize = Size(thickness, thumbExtent);
|
thumbSize = Size(thickness, _thumbExtent);
|
||||||
trackSize = Size(thickness + 2 * crossAxisMargin, _trackExtent);
|
trackSize = Size(thickness + 2 * crossAxisMargin, _trackExtent);
|
||||||
x = crossAxisMargin + padding.left;
|
x = crossAxisMargin + padding.left;
|
||||||
y = _thumbOffset;
|
y = _thumbOffset;
|
||||||
trackOffset = Offset(x - crossAxisMargin, padding.top);
|
trackOffset = Offset(x - crossAxisMargin, _leadingTrackMainAxisOffset);
|
||||||
borderStart = trackOffset + Offset(trackSize.width, 0.0);
|
borderStart = trackOffset + Offset(trackSize.width, 0.0);
|
||||||
borderEnd = Offset(trackOffset.dx + trackSize.width, trackOffset.dy + _trackExtent);
|
borderEnd = Offset(trackOffset.dx + trackSize.width, trackOffset.dy + _trackExtent);
|
||||||
break;
|
break;
|
||||||
case ScrollbarOrientation.right:
|
case ScrollbarOrientation.right:
|
||||||
thumbSize = Size(thickness, thumbExtent);
|
thumbSize = Size(thickness, _thumbExtent);
|
||||||
trackSize = Size(thickness + 2 * crossAxisMargin, _trackExtent);
|
trackSize = Size(thickness + 2 * crossAxisMargin, _trackExtent);
|
||||||
x = size.width - thickness - crossAxisMargin - padding.right;
|
x = size.width - thickness - crossAxisMargin - padding.right;
|
||||||
y = _thumbOffset;
|
y = _thumbOffset;
|
||||||
trackOffset = Offset(x - crossAxisMargin, padding.top);
|
trackOffset = Offset(x - crossAxisMargin, _leadingTrackMainAxisOffset);
|
||||||
borderStart = trackOffset;
|
borderStart = trackOffset;
|
||||||
borderEnd = Offset(trackOffset.dx, trackOffset.dy + _trackExtent);
|
borderEnd = Offset(trackOffset.dx, trackOffset.dy + _trackExtent);
|
||||||
break;
|
break;
|
||||||
case ScrollbarOrientation.top:
|
case ScrollbarOrientation.top:
|
||||||
thumbSize = Size(thumbExtent, thickness);
|
thumbSize = Size(_thumbExtent, thickness);
|
||||||
trackSize = Size(_trackExtent, thickness + 2 * crossAxisMargin);
|
trackSize = Size(_trackExtent, thickness + 2 * crossAxisMargin);
|
||||||
x = _thumbOffset;
|
x = _thumbOffset;
|
||||||
y = crossAxisMargin + padding.top;
|
y = crossAxisMargin + padding.top;
|
||||||
trackOffset = Offset(padding.left, y - crossAxisMargin);
|
trackOffset = Offset(_leadingTrackMainAxisOffset, y - crossAxisMargin);
|
||||||
borderStart = trackOffset + Offset(0.0, trackSize.height);
|
borderStart = trackOffset + Offset(0.0, trackSize.height);
|
||||||
borderEnd = Offset(trackOffset.dx + _trackExtent, trackOffset.dy + trackSize.height);
|
borderEnd = Offset(trackOffset.dx + _trackExtent, trackOffset.dy + trackSize.height);
|
||||||
break;
|
break;
|
||||||
case ScrollbarOrientation.bottom:
|
case ScrollbarOrientation.bottom:
|
||||||
thumbSize = Size(thumbExtent, thickness);
|
thumbSize = Size(_thumbExtent, thickness);
|
||||||
trackSize = Size(_trackExtent, thickness + 2 * crossAxisMargin);
|
trackSize = Size(_trackExtent, thickness + 2 * crossAxisMargin);
|
||||||
x = _thumbOffset;
|
x = _thumbOffset;
|
||||||
y = size.height - thickness - crossAxisMargin - padding.bottom;
|
y = size.height - thickness - crossAxisMargin - padding.bottom;
|
||||||
trackOffset = Offset(padding.left, y - crossAxisMargin);
|
trackOffset = Offset(_leadingTrackMainAxisOffset, y - crossAxisMargin);
|
||||||
borderStart = trackOffset;
|
borderStart = trackOffset;
|
||||||
borderEnd = Offset(trackOffset.dx + _trackExtent, trackOffset.dy);
|
borderEnd = Offset(trackOffset.dx + _trackExtent, trackOffset.dy);
|
||||||
break;
|
break;
|
||||||
@ -557,70 +647,33 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double _thumbExtent() {
|
|
||||||
// Thumb extent reflects fraction of content visible, as long as this
|
|
||||||
// isn't less than the absolute minimum size.
|
|
||||||
// _totalContentExtent >= viewportDimension, so (_totalContentExtent - _mainAxisPadding) > 0
|
|
||||||
final double fractionVisible = clampDouble(
|
|
||||||
(_lastMetrics!.extentInside - _mainAxisPadding) /
|
|
||||||
(_totalContentExtent - _mainAxisPadding),
|
|
||||||
0.0,
|
|
||||||
1.0);
|
|
||||||
|
|
||||||
final double thumbExtent = math.max(
|
|
||||||
math.min(_traversableTrackExtent, minOverscrollLength),
|
|
||||||
_traversableTrackExtent * fractionVisible,
|
|
||||||
);
|
|
||||||
|
|
||||||
final double fractionOverscrolled = 1.0 - _lastMetrics!.extentInside / _lastMetrics!.viewportDimension;
|
|
||||||
final double safeMinLength = math.min(minLength, _traversableTrackExtent);
|
|
||||||
final double newMinLength = (_beforeExtent > 0 && _afterExtent > 0)
|
|
||||||
// Thumb extent is no smaller than minLength if scrolling normally.
|
|
||||||
? safeMinLength
|
|
||||||
// User is overscrolling. Thumb extent can be less than minLength
|
|
||||||
// but no smaller than minOverscrollLength. We can't use the
|
|
||||||
// fractionVisible to produce intermediate values between minLength and
|
|
||||||
// minOverscrollLength when the user is transitioning from regular
|
|
||||||
// scrolling to overscrolling, so we instead use the percentage of the
|
|
||||||
// content that is still in the viewport to determine the size of the
|
|
||||||
// thumb. iOS behavior appears to have the thumb reach its minimum size
|
|
||||||
// with ~20% of overscroll. We map the percentage of minLength from
|
|
||||||
// [0.8, 1.0] to [0.0, 1.0], so 0% to 20% of overscroll will produce
|
|
||||||
// values for the thumb that range between minLength and the smallest
|
|
||||||
// possible value, minOverscrollLength.
|
|
||||||
: safeMinLength * (1.0 - clampDouble(fractionOverscrolled, 0.0, 0.2) / 0.2);
|
|
||||||
|
|
||||||
// The `thumbExtent` should be no greater than `trackSize`, otherwise
|
|
||||||
// the scrollbar may scroll towards the wrong direction.
|
|
||||||
final double extent = clampDouble(thumbExtent, newMinLength, _traversableTrackExtent);
|
|
||||||
return extent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void paint(Canvas canvas, Size size) {
|
||||||
fadeoutOpacityAnimation.removeListener(notifyListeners);
|
if (_lastAxisDirection == null
|
||||||
super.dispose();
|
|| _lastMetrics == null
|
||||||
|
|| _lastMetrics!.maxScrollExtent <= _lastMetrics!.minScrollExtent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Skip painting if there's not enough space.
|
||||||
|
if (_traversableTrackExtent <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Do not paint a scrollbar if the scroll view is infinitely long.
|
||||||
|
// TODO(Piinks): Special handling for infinite scroll views,
|
||||||
|
// https://github.com/flutter/flutter/issues/41434
|
||||||
|
if (_lastMetrics!.maxScrollExtent.isInfinite) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get _isVertical => _lastAxisDirection == AxisDirection.down || _lastAxisDirection == AxisDirection.up;
|
_setThumbExtent();
|
||||||
bool get _isReversed => _lastAxisDirection == AxisDirection.up || _lastAxisDirection == AxisDirection.left;
|
final double thumbPositionOffset = _getScrollToTrack(_lastMetrics!, _thumbExtent);
|
||||||
// The amount of scroll distance before and after the current position.
|
_thumbOffset = thumbPositionOffset + _leadingThumbMainAxisOffset;
|
||||||
double get _beforeExtent => _isReversed ? _lastMetrics!.extentAfter : _lastMetrics!.extentBefore;
|
|
||||||
double get _afterExtent => _isReversed ? _lastMetrics!.extentBefore : _lastMetrics!.extentAfter;
|
|
||||||
// Padding of the thumb track.
|
|
||||||
double get _mainAxisPadding => _isVertical ? padding.vertical : padding.horizontal;
|
|
||||||
// The length of the painted track.
|
|
||||||
double get _trackExtent => _lastMetrics!.viewportDimension - _mainAxisPadding;
|
|
||||||
// The length of the track that is traversable by the thumb.
|
|
||||||
double get _traversableTrackExtent => _trackExtent - (2 * mainAxisMargin);
|
|
||||||
|
|
||||||
// The total size of the scrollable content.
|
return _paintScrollbar(canvas, size);
|
||||||
double get _totalContentExtent {
|
|
||||||
return _lastMetrics!.maxScrollExtent
|
|
||||||
- _lastMetrics!.minScrollExtent
|
|
||||||
+ _lastMetrics!.viewportDimension;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - Scroll Position Conversion
|
||||||
|
|
||||||
/// Convert between a thumb track position and the corresponding scroll
|
/// Convert between a thumb track position and the corresponding scroll
|
||||||
/// position.
|
/// position.
|
||||||
///
|
///
|
||||||
@ -628,7 +681,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
|
|||||||
double getTrackToScroll(double thumbOffsetLocal) {
|
double getTrackToScroll(double thumbOffsetLocal) {
|
||||||
assert(thumbOffsetLocal != null);
|
assert(thumbOffsetLocal != null);
|
||||||
final double scrollableExtent = _lastMetrics!.maxScrollExtent - _lastMetrics!.minScrollExtent;
|
final double scrollableExtent = _lastMetrics!.maxScrollExtent - _lastMetrics!.minScrollExtent;
|
||||||
final double thumbMovableExtent = _traversableTrackExtent - _thumbExtent();
|
final double thumbMovableExtent = _traversableTrackExtent - _thumbExtent;
|
||||||
|
|
||||||
return scrollableExtent * thumbOffsetLocal / thumbMovableExtent;
|
return scrollableExtent * thumbOffsetLocal / thumbMovableExtent;
|
||||||
}
|
}
|
||||||
@ -645,35 +698,27 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
|
|||||||
return (_isReversed ? 1 - fractionPast : fractionPast) * (_traversableTrackExtent - thumbExtent);
|
return (_isReversed ? 1 - fractionPast : fractionPast) * (_traversableTrackExtent - thumbExtent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - Hit Testing
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
bool? hitTest(Offset? position) {
|
||||||
if (_lastAxisDirection == null
|
// There is nothing painted to hit.
|
||||||
|| _lastMetrics == null
|
if (_thumbRect == null) {
|
||||||
|| _lastMetrics!.maxScrollExtent <= _lastMetrics!.minScrollExtent) {
|
return null;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip painting if there's not enough space.
|
// Interaction disabled.
|
||||||
if (_lastMetrics!.viewportDimension <= _mainAxisPadding || _traversableTrackExtent <= 0) {
|
if (ignorePointer
|
||||||
return;
|
// The thumb is not able to be hit when transparent.
|
||||||
|
|| fadeoutOpacityAnimation.value == 0.0
|
||||||
|
// Not scrollable
|
||||||
|
|| !_lastMetricsAreScrollable) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final double beforePadding = _isVertical ? padding.top : padding.left;
|
return _trackRect!.contains(position!);
|
||||||
final double thumbExtent = _thumbExtent();
|
|
||||||
final double thumbOffsetLocal = _getScrollToTrack(_lastMetrics!, thumbExtent);
|
|
||||||
_thumbOffset = thumbOffsetLocal + mainAxisMargin + beforePadding;
|
|
||||||
|
|
||||||
// Do not paint a scrollbar if the scroll view is infinitely long.
|
|
||||||
// TODO(Piinks): Special handling for infinite scroll views, https://github.com/flutter/flutter/issues/41434
|
|
||||||
if (_lastMetrics!.maxScrollExtent.isInfinite) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _paintScrollbar(canvas, size, thumbExtent, _lastAxisDirection!);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get _lastMetricsAreScrollable => _lastMetrics!.minScrollExtent != _lastMetrics!.maxScrollExtent;
|
|
||||||
|
|
||||||
/// Same as hitTest, but includes some padding when the [PointerEvent] is
|
/// Same as hitTest, but includes some padding when the [PointerEvent] is
|
||||||
/// caused by [PointerDeviceKind.touch] to make sure that the region
|
/// caused by [PointerDeviceKind.touch] to make sure that the region
|
||||||
/// isn't too small to be interacted with by the user.
|
/// isn't too small to be interacted with by the user.
|
||||||
@ -756,28 +801,6 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scrollbars are interactive.
|
|
||||||
@override
|
|
||||||
bool? hitTest(Offset? position) {
|
|
||||||
if (_thumbRect == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (ignorePointer) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The thumb is not able to be hit when transparent.
|
|
||||||
if (fadeoutOpacityAnimation.value == 0.0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_lastMetricsAreScrollable) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _trackRect!.contains(position!);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool shouldRepaint(ScrollbarPainter oldDelegate) {
|
bool shouldRepaint(ScrollbarPainter oldDelegate) {
|
||||||
// Should repaint if any properties changed.
|
// Should repaint if any properties changed.
|
||||||
@ -807,6 +830,12 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => describeIdentity(this);
|
String toString() => describeIdentity(this);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
fadeoutOpacityAnimation.removeListener(notifyListeners);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An extendable base class for building scrollbars that fade in and out.
|
/// An extendable base class for building scrollbars that fade in and out.
|
||||||
@ -1669,7 +1698,7 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
case TargetPlatform.iOS:
|
case TargetPlatform.iOS:
|
||||||
case TargetPlatform.android:
|
case TargetPlatform.android:
|
||||||
// We can only drag the scrollbar into overscroll on mobile
|
// We can only drag the scrollbar into overscroll on mobile
|
||||||
// platforms, and only if the physics allow it.
|
// platforms, and only then if the physics allow it.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
position.jumpTo(newPosition);
|
position.jumpTo(newPosition);
|
||||||
@ -1908,7 +1937,7 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
() => _ThumbPressGestureRecognizer(
|
() => _ThumbPressGestureRecognizer(
|
||||||
debugOwner: this,
|
debugOwner: this,
|
||||||
customPaintKey: _scrollbarPainterKey,
|
customPaintKey: _scrollbarPainterKey,
|
||||||
pressDuration: widget.pressDuration,
|
duration: widget.pressDuration,
|
||||||
),
|
),
|
||||||
(_ThumbPressGestureRecognizer instance) {
|
(_ThumbPressGestureRecognizer instance) {
|
||||||
instance.onLongPress = handleThumbPress;
|
instance.onLongPress = handleThumbPress;
|
||||||
@ -2074,11 +2103,8 @@ class _ThumbPressGestureRecognizer extends LongPressGestureRecognizer {
|
|||||||
_ThumbPressGestureRecognizer({
|
_ThumbPressGestureRecognizer({
|
||||||
required Object super.debugOwner,
|
required Object super.debugOwner,
|
||||||
required GlobalKey customPaintKey,
|
required GlobalKey customPaintKey,
|
||||||
required Duration pressDuration,
|
required super.duration,
|
||||||
}) : _customPaintKey = customPaintKey,
|
}) : _customPaintKey = customPaintKey;
|
||||||
super(
|
|
||||||
duration: pressDuration,
|
|
||||||
);
|
|
||||||
|
|
||||||
final GlobalKey _customPaintKey;
|
final GlobalKey _customPaintKey;
|
||||||
|
|
||||||
@ -2105,10 +2131,9 @@ class _ThumbPressGestureRecognizer extends LongPressGestureRecognizer {
|
|||||||
// track and ignores everything else, including the thumb.
|
// track and ignores everything else, including the thumb.
|
||||||
class _TrackTapGestureRecognizer extends TapGestureRecognizer {
|
class _TrackTapGestureRecognizer extends TapGestureRecognizer {
|
||||||
_TrackTapGestureRecognizer({
|
_TrackTapGestureRecognizer({
|
||||||
required Object debugOwner,
|
required super.debugOwner,
|
||||||
required GlobalKey customPaintKey,
|
required GlobalKey customPaintKey,
|
||||||
}) : _customPaintKey = customPaintKey,
|
}) : _customPaintKey = customPaintKey;
|
||||||
super(debugOwner: debugOwner);
|
|
||||||
|
|
||||||
final GlobalKey _customPaintKey;
|
final GlobalKey _customPaintKey;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user