mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[New Feature]Support mouse wheel event on the scrollbar widget (#109659)
* rebase master and add a test * fix the test * fix the test * fix the test
This commit is contained in:
parent
96f9ca8302
commit
1aada6fc5c
@ -1458,13 +1458,17 @@ class RawScrollbar extends StatefulWidget {
|
|||||||
/// scrollbar track.
|
/// scrollbar track.
|
||||||
class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProviderStateMixin<T> {
|
class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProviderStateMixin<T> {
|
||||||
Offset? _startDragScrollbarAxisOffset;
|
Offset? _startDragScrollbarAxisOffset;
|
||||||
|
Offset? _lastDragUpdateOffset;
|
||||||
double? _startDragThumbOffset;
|
double? _startDragThumbOffset;
|
||||||
ScrollController? _currentController;
|
ScrollController? _cachedController;
|
||||||
Timer? _fadeoutTimer;
|
Timer? _fadeoutTimer;
|
||||||
late AnimationController _fadeoutAnimationController;
|
late AnimationController _fadeoutAnimationController;
|
||||||
late Animation<double> _fadeoutOpacityAnimation;
|
late Animation<double> _fadeoutOpacityAnimation;
|
||||||
final GlobalKey _scrollbarPainterKey = GlobalKey();
|
final GlobalKey _scrollbarPainterKey = GlobalKey();
|
||||||
bool _hoverIsActive = false;
|
bool _hoverIsActive = false;
|
||||||
|
bool _thumbDragging = false;
|
||||||
|
|
||||||
|
ScrollController? get _effectiveScrollController => widget.controller ?? PrimaryScrollController.maybeOf(context);
|
||||||
|
|
||||||
/// Used to paint the scrollbar.
|
/// Used to paint the scrollbar.
|
||||||
///
|
///
|
||||||
@ -1550,12 +1554,11 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _validateInteractions(AnimationStatus status) {
|
void _validateInteractions(AnimationStatus status) {
|
||||||
final ScrollController? scrollController = widget.controller ?? PrimaryScrollController.maybeOf(context);
|
|
||||||
if (status == AnimationStatus.dismissed) {
|
if (status == AnimationStatus.dismissed) {
|
||||||
assert(_fadeoutOpacityAnimation.value == 0.0);
|
assert(_fadeoutOpacityAnimation.value == 0.0);
|
||||||
// We do not check for a valid scroll position if the scrollbar is not
|
// We do not check for a valid scroll position if the scrollbar is not
|
||||||
// visible, because it cannot be interacted with.
|
// visible, because it cannot be interacted with.
|
||||||
} else if (scrollController != null && enableGestures) {
|
} else if (_effectiveScrollController != null && enableGestures) {
|
||||||
// Interactive scrollbars need to be properly configured. If it is visible
|
// Interactive scrollbars need to be properly configured. If it is visible
|
||||||
// for interaction, ensure we are set up properly.
|
// for interaction, ensure we are set up properly.
|
||||||
assert(_debugCheckHasValidScrollPosition());
|
assert(_debugCheckHasValidScrollPosition());
|
||||||
@ -1566,7 +1569,7 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
final ScrollController? scrollController = widget.controller ?? PrimaryScrollController.maybeOf(context);
|
final ScrollController? scrollController = _effectiveScrollController;
|
||||||
final bool tryPrimary = widget.controller == null;
|
final bool tryPrimary = widget.controller == null;
|
||||||
final String controllerForError = tryPrimary
|
final String controllerForError = tryPrimary
|
||||||
? 'PrimaryScrollController'
|
? 'PrimaryScrollController'
|
||||||
@ -1698,11 +1701,11 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _updateScrollPosition(Offset updatedOffset) {
|
void _updateScrollPosition(Offset updatedOffset) {
|
||||||
assert(_currentController != null);
|
assert(_cachedController != null);
|
||||||
assert(_startDragScrollbarAxisOffset != null);
|
assert(_startDragScrollbarAxisOffset != null);
|
||||||
assert(_startDragThumbOffset != null);
|
assert(_startDragThumbOffset != null);
|
||||||
|
|
||||||
final ScrollPosition position = _currentController!.position;
|
final ScrollPosition position = _cachedController!.position;
|
||||||
late double primaryDelta;
|
late double primaryDelta;
|
||||||
switch (position.axisDirection) {
|
switch (position.axisDirection) {
|
||||||
case AxisDirection.up:
|
case AxisDirection.up:
|
||||||
@ -1761,9 +1764,9 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
/// current scroll controller does not have any attached positions.
|
/// current scroll controller does not have any attached positions.
|
||||||
@protected
|
@protected
|
||||||
Axis? getScrollbarDirection() {
|
Axis? getScrollbarDirection() {
|
||||||
assert(_currentController != null);
|
assert(_cachedController != null);
|
||||||
if (_currentController!.hasClients) {
|
if (_cachedController!.hasClients) {
|
||||||
return _currentController!.position.axis;
|
return _cachedController!.position.axis;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -1788,7 +1791,7 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
@mustCallSuper
|
@mustCallSuper
|
||||||
void handleThumbPressStart(Offset localPosition) {
|
void handleThumbPressStart(Offset localPosition) {
|
||||||
assert(_debugCheckHasValidScrollPosition());
|
assert(_debugCheckHasValidScrollPosition());
|
||||||
_currentController = widget.controller ?? PrimaryScrollController.maybeOf(context);
|
_cachedController = _effectiveScrollController;
|
||||||
final Axis? direction = getScrollbarDirection();
|
final Axis? direction = getScrollbarDirection();
|
||||||
if (direction == null) {
|
if (direction == null) {
|
||||||
return;
|
return;
|
||||||
@ -1797,6 +1800,7 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
_fadeoutAnimationController.forward();
|
_fadeoutAnimationController.forward();
|
||||||
_startDragScrollbarAxisOffset = localPosition;
|
_startDragScrollbarAxisOffset = localPosition;
|
||||||
_startDragThumbOffset = scrollbarPainter.getThumbScrollOffset();
|
_startDragThumbOffset = scrollbarPainter.getThumbScrollOffset();
|
||||||
|
_thumbDragging = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler called when a currently active long press gesture moves.
|
/// Handler called when a currently active long press gesture moves.
|
||||||
@ -1806,7 +1810,11 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
@mustCallSuper
|
@mustCallSuper
|
||||||
void handleThumbPressUpdate(Offset localPosition) {
|
void handleThumbPressUpdate(Offset localPosition) {
|
||||||
assert(_debugCheckHasValidScrollPosition());
|
assert(_debugCheckHasValidScrollPosition());
|
||||||
final ScrollPosition position = _currentController!.position;
|
if (_lastDragUpdateOffset == localPosition) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_lastDragUpdateOffset = localPosition;
|
||||||
|
final ScrollPosition position = _cachedController!.position;
|
||||||
if (!position.physics.shouldAcceptUserOffset(position)) {
|
if (!position.physics.shouldAcceptUserOffset(position)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1822,22 +1830,24 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
@mustCallSuper
|
@mustCallSuper
|
||||||
void handleThumbPressEnd(Offset localPosition, Velocity velocity) {
|
void handleThumbPressEnd(Offset localPosition, Velocity velocity) {
|
||||||
assert(_debugCheckHasValidScrollPosition());
|
assert(_debugCheckHasValidScrollPosition());
|
||||||
|
_thumbDragging = false;
|
||||||
final Axis? direction = getScrollbarDirection();
|
final Axis? direction = getScrollbarDirection();
|
||||||
if (direction == null) {
|
if (direction == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_maybeStartFadeoutTimer();
|
_maybeStartFadeoutTimer();
|
||||||
_startDragScrollbarAxisOffset = null;
|
_startDragScrollbarAxisOffset = null;
|
||||||
|
_lastDragUpdateOffset = null;
|
||||||
_startDragThumbOffset = null;
|
_startDragThumbOffset = null;
|
||||||
_currentController = null;
|
_cachedController = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleTrackTapDown(TapDownDetails details) {
|
void _handleTrackTapDown(TapDownDetails details) {
|
||||||
// The Scrollbar should page towards the position of the tap on the track.
|
// The Scrollbar should page towards the position of the tap on the track.
|
||||||
assert(_debugCheckHasValidScrollPosition());
|
assert(_debugCheckHasValidScrollPosition());
|
||||||
_currentController = widget.controller ?? PrimaryScrollController.maybeOf(context);
|
_cachedController = _effectiveScrollController;
|
||||||
|
|
||||||
final ScrollPosition position = _currentController!.position;
|
final ScrollPosition position = _cachedController!.position;
|
||||||
if (!position.physics.shouldAcceptUserOffset(position)) {
|
if (!position.physics.shouldAcceptUserOffset(position)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1845,22 +1855,22 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
double scrollIncrement;
|
double scrollIncrement;
|
||||||
// Is an increment calculator available?
|
// Is an increment calculator available?
|
||||||
final ScrollIncrementCalculator? calculator = Scrollable.maybeOf(
|
final ScrollIncrementCalculator? calculator = Scrollable.maybeOf(
|
||||||
_currentController!.position.context.notificationContext!,
|
_cachedController!.position.context.notificationContext!,
|
||||||
)?.widget.incrementCalculator;
|
)?.widget.incrementCalculator;
|
||||||
if (calculator != null) {
|
if (calculator != null) {
|
||||||
scrollIncrement = calculator(
|
scrollIncrement = calculator(
|
||||||
ScrollIncrementDetails(
|
ScrollIncrementDetails(
|
||||||
type: ScrollIncrementType.page,
|
type: ScrollIncrementType.page,
|
||||||
metrics: _currentController!.position,
|
metrics: _cachedController!.position,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Default page increment
|
// Default page increment
|
||||||
scrollIncrement = 0.8 * _currentController!.position.viewportDimension;
|
scrollIncrement = 0.8 * _cachedController!.position.viewportDimension;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust scrollIncrement for direction
|
// Adjust scrollIncrement for direction
|
||||||
switch (_currentController!.position.axisDirection) {
|
switch (_cachedController!.position.axisDirection) {
|
||||||
case AxisDirection.up:
|
case AxisDirection.up:
|
||||||
if (details.localPosition.dy > scrollbarPainter._thumbOffset) {
|
if (details.localPosition.dy > scrollbarPainter._thumbOffset) {
|
||||||
scrollIncrement = -scrollIncrement;
|
scrollIncrement = -scrollIncrement;
|
||||||
@ -1883,8 +1893,8 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentController!.position.moveTo(
|
_cachedController!.position.moveTo(
|
||||||
_currentController!.position.pixels + scrollIncrement,
|
_cachedController!.position.pixels + scrollIncrement,
|
||||||
duration: const Duration(milliseconds: 100),
|
duration: const Duration(milliseconds: 100),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
@ -1892,8 +1902,7 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
|
|
||||||
// ScrollController takes precedence over ScrollNotification
|
// ScrollController takes precedence over ScrollNotification
|
||||||
bool _shouldUpdatePainter(Axis notificationAxis) {
|
bool _shouldUpdatePainter(Axis notificationAxis) {
|
||||||
final ScrollController? scrollController = widget.controller ??
|
final ScrollController? scrollController = _effectiveScrollController;
|
||||||
PrimaryScrollController.maybeOf(context);
|
|
||||||
// Only update the painter of this scrollbar if the notification
|
// Only update the painter of this scrollbar if the notification
|
||||||
// metrics do not conflict with the information we have from the scroll
|
// metrics do not conflict with the information we have from the scroll
|
||||||
// controller.
|
// controller.
|
||||||
@ -1979,8 +1988,7 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
|
|
||||||
Map<Type, GestureRecognizerFactory> get _gestures {
|
Map<Type, GestureRecognizerFactory> get _gestures {
|
||||||
final Map<Type, GestureRecognizerFactory> gestures = <Type, GestureRecognizerFactory>{};
|
final Map<Type, GestureRecognizerFactory> gestures = <Type, GestureRecognizerFactory>{};
|
||||||
final ScrollController? controller = widget.controller ?? PrimaryScrollController.maybeOf(context);
|
if (_effectiveScrollController == null || !enableGestures) {
|
||||||
if (controller == null || !enableGestures) {
|
|
||||||
return gestures;
|
return gestures;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2086,6 +2094,64 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
_maybeStartFadeoutTimer();
|
_maybeStartFadeoutTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the delta that should result from applying [event] with axis and
|
||||||
|
// direction taken into account.
|
||||||
|
double _pointerSignalEventDelta(PointerScrollEvent event) {
|
||||||
|
assert(_cachedController != null);
|
||||||
|
double delta = _cachedController!.position.axis == Axis.horizontal
|
||||||
|
? event.scrollDelta.dx
|
||||||
|
: event.scrollDelta.dy;
|
||||||
|
|
||||||
|
if (axisDirectionIsReversed(_cachedController!.position.axisDirection)) {
|
||||||
|
delta *= -1;
|
||||||
|
}
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the offset that should result from applying [event] to the current
|
||||||
|
// position, taking min/max scroll extent into account.
|
||||||
|
double _targetScrollOffsetForPointerScroll(double delta) {
|
||||||
|
assert(_cachedController != null);
|
||||||
|
return math.min(
|
||||||
|
math.max(_cachedController!.position.pixels + delta, _cachedController!.position.minScrollExtent),
|
||||||
|
_cachedController!.position.maxScrollExtent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handlePointerScroll(PointerEvent event) {
|
||||||
|
assert(event is PointerScrollEvent);
|
||||||
|
_cachedController = _effectiveScrollController;
|
||||||
|
final double delta = _pointerSignalEventDelta(event as PointerScrollEvent);
|
||||||
|
final double targetScrollOffset = _targetScrollOffsetForPointerScroll(delta);
|
||||||
|
if (delta != 0.0 && targetScrollOffset != _cachedController!.position.pixels) {
|
||||||
|
_cachedController!.position.pointerScroll(delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _receivedPointerSignal(PointerSignalEvent event) {
|
||||||
|
_cachedController = _effectiveScrollController;
|
||||||
|
// Only try to scroll if the bar absorb the hit test.
|
||||||
|
if ((scrollbarPainter.hitTest(event.localPosition) ?? false) &&
|
||||||
|
_cachedController != null &&
|
||||||
|
_cachedController!.hasClients &&
|
||||||
|
(!_thumbDragging || kIsWeb)) {
|
||||||
|
final ScrollPosition position = _cachedController!.position;
|
||||||
|
if (event is PointerScrollEvent && position != null) {
|
||||||
|
if (!position.physics.shouldAcceptUserOffset(position)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final double delta = _pointerSignalEventDelta(event);
|
||||||
|
final double targetScrollOffset = _targetScrollOffsetForPointerScroll(delta);
|
||||||
|
if (delta != 0.0 && targetScrollOffset != position.pixels) {
|
||||||
|
GestureBinding.instance.pointerSignalResolver.register(event, _handlePointerScroll);
|
||||||
|
}
|
||||||
|
} else if (event is PointerScrollInertiaCancelEvent) {
|
||||||
|
position.jumpTo(position.pixels);
|
||||||
|
// Don't use the pointer signal resolver, all hit-tested scrollables should stop.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_fadeoutAnimationController.dispose();
|
_fadeoutAnimationController.dispose();
|
||||||
@ -2103,6 +2169,8 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
child: NotificationListener<ScrollNotification>(
|
child: NotificationListener<ScrollNotification>(
|
||||||
onNotification: _handleScrollNotification,
|
onNotification: _handleScrollNotification,
|
||||||
child: RepaintBoundary(
|
child: RepaintBoundary(
|
||||||
|
child: Listener(
|
||||||
|
onPointerSignal: _receivedPointerSignal,
|
||||||
child: RawGestureDetector(
|
child: RawGestureDetector(
|
||||||
gestures: _gestures,
|
gestures: _gestures,
|
||||||
child: MouseRegion(
|
child: MouseRegion(
|
||||||
@ -2145,6 +2213,7 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1200,6 +1200,8 @@ void main() {
|
|||||||
pointer.hover(const Offset(793.0, 15.0));
|
pointer.hover(const Offset(793.0, 15.0));
|
||||||
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 20.0)));
|
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 20.0)));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
if (!kIsWeb) {
|
||||||
// Scrolling while holding the drag on the scrollbar and still hovered over
|
// Scrolling while holding the drag on the scrollbar and still hovered over
|
||||||
// the scrollbar should not have changed the scroll offset.
|
// the scrollbar should not have changed the scroll offset.
|
||||||
expect(pointer.location, const Offset(793.0, 15.0));
|
expect(pointer.location, const Offset(793.0, 15.0));
|
||||||
@ -1214,11 +1216,16 @@ void main() {
|
|||||||
color: _kScrollbarColor.color,
|
color: _kScrollbarColor.color,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
expect(pointer.location, const Offset(793.0, 15.0));
|
||||||
|
expect(scrollController.offset, previousOffset + 20.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Drag is still being held, move pointer to be hovering over another area
|
// Drag is still being held, move pointer to be hovering over another area
|
||||||
// of the scrollable (not over the scrollbar) and execute another pointer scroll
|
// of the scrollable (not over the scrollbar) and execute another pointer scroll
|
||||||
pointer.hover(tester.getCenter(find.byType(SingleChildScrollView)));
|
pointer.hover(tester.getCenter(find.byType(SingleChildScrollView)));
|
||||||
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, -70.0)));
|
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, -90.0)));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
// Scrolling while holding the drag on the scrollbar changed the offset
|
// Scrolling while holding the drag on the scrollbar changed the offset
|
||||||
expect(pointer.location, const Offset(400.0, 300.0));
|
expect(pointer.location, const Offset(400.0, 300.0));
|
||||||
|
@ -1634,6 +1634,8 @@ void main() {
|
|||||||
pointer.hover(const Offset(798.0, 15.0));
|
pointer.hover(const Offset(798.0, 15.0));
|
||||||
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 20.0)));
|
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 20.0)));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
if (!kIsWeb) {
|
||||||
// Scrolling while holding the drag on the scrollbar and still hovered over
|
// Scrolling while holding the drag on the scrollbar and still hovered over
|
||||||
// the scrollbar should not have changed the scroll offset.
|
// the scrollbar should not have changed the scroll offset.
|
||||||
expect(pointer.location, const Offset(798.0, 15.0));
|
expect(pointer.location, const Offset(798.0, 15.0));
|
||||||
@ -1656,11 +1658,15 @@ void main() {
|
|||||||
color: const Color(0x99000000),
|
color: const Color(0x99000000),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
expect(pointer.location, const Offset(798.0, 15.0));
|
||||||
|
expect(scrollController.offset, previousOffset + 20.0);
|
||||||
|
}
|
||||||
|
|
||||||
// Drag is still being held, move pointer to be hovering over another area
|
// Drag is still being held, move pointer to be hovering over another area
|
||||||
// of the scrollable (not over the scrollbar) and execute another pointer scroll
|
// of the scrollable (not over the scrollbar) and execute another pointer scroll
|
||||||
pointer.hover(tester.getCenter(find.byType(SingleChildScrollView)));
|
pointer.hover(tester.getCenter(find.byType(SingleChildScrollView)));
|
||||||
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, -70.0)));
|
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, -90.0)));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
// Scrolling while holding the drag on the scrollbar changed the offset
|
// Scrolling while holding the drag on the scrollbar changed the offset
|
||||||
expect(pointer.location, const Offset(400.0, 300.0));
|
expect(pointer.location, const Offset(400.0, 300.0));
|
||||||
|
@ -1607,6 +1607,8 @@ void main() {
|
|||||||
pointer.hover(const Offset(798.0, 15.0));
|
pointer.hover(const Offset(798.0, 15.0));
|
||||||
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 20.0)));
|
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 20.0)));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
if (!kIsWeb) {
|
||||||
// Scrolling while holding the drag on the scrollbar and still hovered over
|
// Scrolling while holding the drag on the scrollbar and still hovered over
|
||||||
// the scrollbar should not have changed the scroll offset.
|
// the scrollbar should not have changed the scroll offset.
|
||||||
expect(pointer.location, const Offset(798.0, 15.0));
|
expect(pointer.location, const Offset(798.0, 15.0));
|
||||||
@ -1629,11 +1631,15 @@ void main() {
|
|||||||
color: const Color(0x66bcbcbc),
|
color: const Color(0x66bcbcbc),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
expect(pointer.location, const Offset(798.0, 15.0));
|
||||||
|
expect(scrollController.offset, previousOffset + 20.0);
|
||||||
|
}
|
||||||
|
|
||||||
// Drag is still being held, move pointer to be hovering over another area
|
// Drag is still being held, move pointer to be hovering over another area
|
||||||
// of the scrollable (not over the scrollbar) and execute another pointer scroll
|
// of the scrollable (not over the scrollbar) and execute another pointer scroll
|
||||||
pointer.hover(tester.getCenter(find.byType(SingleChildScrollView)));
|
pointer.hover(tester.getCenter(find.byType(SingleChildScrollView)));
|
||||||
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, -70.0)));
|
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, -90.0)));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
// Scrolling while holding the drag on the scrollbar changed the offset
|
// Scrolling while holding the drag on the scrollbar changed the offset
|
||||||
expect(pointer.location, const Offset(400.0, 300.0));
|
expect(pointer.location, const Offset(400.0, 300.0));
|
||||||
@ -2788,4 +2794,50 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('The bar support mouse wheel event', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/pull/109659
|
||||||
|
final ScrollController scrollController = ScrollController();
|
||||||
|
Widget buildFrame() {
|
||||||
|
return Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: MediaQuery(
|
||||||
|
data: const MediaQueryData(),
|
||||||
|
child: PrimaryScrollController(
|
||||||
|
controller: scrollController,
|
||||||
|
child: RawScrollbar(
|
||||||
|
thumbVisibility: true,
|
||||||
|
controller: scrollController,
|
||||||
|
child: const SingleChildScrollView(
|
||||||
|
primary: true,
|
||||||
|
child: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 1200.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame());
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(scrollController.offset, 0.0);
|
||||||
|
|
||||||
|
// Execute a pointer scroll hover on the scroll bar
|
||||||
|
final TestPointer pointer = TestPointer(1, ui.PointerDeviceKind.mouse);
|
||||||
|
pointer.hover(const Offset(798.0, 15.0));
|
||||||
|
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 30.0)));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(scrollController.offset, 30.0);
|
||||||
|
|
||||||
|
// Execute a pointer scroll outside the scroll bar
|
||||||
|
pointer.hover(const Offset(198.0, 15.0));
|
||||||
|
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 70.0)));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(scrollController.offset, 100.0);
|
||||||
|
}, variant: TargetPlatformVariant.all());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user