Unverified Commit 90f93a5a authored by nt4f04uNd's avatar nt4f04uNd Committed by GitHub

Allow to click through scrollbar when gestures are disabled (#91532)

parent ee204880
......@@ -348,7 +348,8 @@ class _MaterialScrollbarState extends RawScrollbarState<_MaterialScrollbar> {
..mainAxisMargin = _scrollbarTheme.mainAxisMargin ?? 0.0
..minLength = _scrollbarTheme.minThumbLength ?? _kScrollbarMinLength
..padding = MediaQuery.of(context).padding
..scrollbarOrientation = widget.scrollbarOrientation;
..scrollbarOrientation = widget.scrollbarOrientation
..ignorePointer = !enableGestures;
}
@override
......
......@@ -90,6 +90,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
double minLength = _kMinThumbExtent,
double? minOverscrollLength,
ScrollbarOrientation? scrollbarOrientation,
bool ignorePointer = false,
}) : assert(color != null),
assert(radius == null || shape == null),
assert(thickness != null),
......@@ -102,6 +103,9 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
assert(minOverscrollLength == null || minOverscrollLength >= 0),
assert(padding != null),
assert(padding.isNonNegative),
assert(trackColor != null),
assert(trackBorderColor != null),
assert(ignorePointer != null),
_color = color,
_textDirection = textDirection,
_thickness = thickness,
......@@ -114,7 +118,8 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
_trackColor = trackColor,
_trackBorderColor = trackBorderColor,
_scrollbarOrientation = scrollbarOrientation,
_minOverscrollLength = minOverscrollLength ?? minLength {
_minOverscrollLength = minOverscrollLength ?? minLength,
_ignorePointer = ignorePointer {
fadeoutOpacityAnimation.addListener(notifyListeners);
}
......@@ -341,6 +346,17 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
notifyListeners();
}
/// Whether the painter will be ignored during hit testing.
bool get ignorePointer => _ignorePointer;
bool _ignorePointer;
set ignorePointer(bool value) {
if (ignorePointer == value)
return;
_ignorePointer = value;
notifyListeners();
}
void _debugAssertIsValidOrientation(ScrollbarOrientation orientation) {
assert(
(_isVertical && _isVerticalOrientation(orientation)) || (!_isVertical && !_isVerticalOrientation(orientation)),
......@@ -623,6 +639,9 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
// We have not computed the scrollbar position yet.
return false;
}
if (ignorePointer) {
return false;
}
if (!_lastMetricsAreScrollable) {
return false;
......@@ -659,6 +678,9 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
if (_thumbRect == null) {
return false;
}
if (ignorePointer) {
return false;
}
// The thumb is not able to be hit when transparent.
if (fadeoutOpacityAnimation.value == 0.0) {
return false;
......@@ -688,6 +710,9 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
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;
......@@ -716,7 +741,8 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
|| padding != oldDelegate.padding
|| minLength != oldDelegate.minLength
|| minOverscrollLength != oldDelegate.minOverscrollLength
|| scrollbarOrientation != oldDelegate.scrollbarOrientation;
|| scrollbarOrientation != oldDelegate.scrollbarOrientation
|| ignorePointer != oldDelegate.ignorePointer;
}
@override
......@@ -1104,7 +1130,8 @@ class RawScrollbar extends StatefulWidget {
/// match native behavior. On Android, the scrollbar is not interactive by
/// default.
///
/// When false, the scrollbar will not respond to gesture or hover events.
/// When false, the scrollbar will not respond to gesture or hover events,
/// and will allow to click through it.
///
/// Defaults to true when null, unless on Android, which will default to false
/// when null.
......@@ -1176,6 +1203,9 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
/// Overridable getter to indicate is gestures should be enabled on the
/// scrollbar.
///
/// When false, the scrollbar will not respond to gesture or hover events,
/// and will allow to click through it.
///
/// Subclasses can override this getter to make its value depend on an inherited
/// theme.
///
......@@ -1200,14 +1230,15 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
);
scrollbarPainter = ScrollbarPainter(
color: widget.thumbColor ?? const Color(0x66BCBCBC),
minLength: widget.minThumbLength,
minOverscrollLength: widget.minOverscrollLength ?? widget.minThumbLength,
thickness: widget.thickness ?? _kScrollbarThickness,
fadeoutOpacityAnimation: _fadeoutOpacityAnimation,
thickness: widget.thickness ?? _kScrollbarThickness,
radius: widget.radius,
scrollbarOrientation: widget.scrollbarOrientation,
mainAxisMargin: widget.mainAxisMargin,
shape: widget.shape,
crossAxisMargin: widget.crossAxisMargin
crossAxisMargin: widget.crossAxisMargin,
minLength: widget.minThumbLength,
minOverscrollLength: widget.minOverscrollLength ?? widget.minThumbLength,
);
}
......@@ -1342,7 +1373,8 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
..shape = widget.shape
..crossAxisMargin = widget.crossAxisMargin
..minLength = widget.minThumbLength
..minOverscrollLength = widget.minOverscrollLength ?? widget.minThumbLength;
..minOverscrollLength = widget.minOverscrollLength ?? widget.minThumbLength
..ignorePointer = !enableGestures;
}
@override
......
......@@ -12,6 +12,7 @@ import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_test/flutter_test.dart';
......@@ -1213,6 +1214,7 @@ void main() {
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.fuchsia }));
testWidgets('Scrollbar dragging is disabled by default on Android', (WidgetTester tester) async {
int tapCount = 0;
final ScrollController scrollController = ScrollController();
await tester.pumpWidget(
MaterialApp(
......@@ -1221,8 +1223,18 @@ void main() {
child: Scrollbar(
isAlwaysShown: true,
controller: scrollController,
child: const SingleChildScrollView(
child: SizedBox(width: 4000.0, height: 4000.0),
child: SingleChildScrollView(
dragStartBehavior: DragStartBehavior.down,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
tapCount += 1;
},
child: const SizedBox(
width: 4000.0,
height: 4000.0,
),
),
),
),
),
......@@ -1250,30 +1262,49 @@ void main() {
);
// Try to drag the thumb down.
const double scrollAmount = 10.0;
final TestGesture dragScrollbarThumbGesture = await tester.startGesture(const Offset(797.0, 45.0));
await tester.pumpAndSettle();
await dragScrollbarThumbGesture.moveBy(const Offset(0.0, scrollAmount));
await tester.pumpAndSettle();
await dragScrollbarThumbGesture.up();
const double scrollAmount = 50.0;
await tester.dragFrom(
const Offset(797.0, 45.0),
const Offset(0.0, scrollAmount),
touchSlopY: 0.0,
);
await tester.pumpAndSettle();
// Dragging on the thumb does not change the offset.
expect(scrollController.offset, 0.0);
expect(tapCount, 0);
// Drag in the track area to validate pass through to scrollable.
final TestGesture dragPassThroughTrack = await tester.startGesture(const Offset(797.0, 250.0));
await dragPassThroughTrack.moveBy(const Offset(0.0, -scrollAmount));
await tester.pumpAndSettle();
await dragPassThroughTrack.up();
// Try to drag up in the thumb area to validate pass through to scrollable.
await tester.dragFrom(
const Offset(797.0, 45.0),
const Offset(0.0, -scrollAmount),
);
await tester.pumpAndSettle();
// The scroll view received the drag.
expect(scrollController.offset, scrollAmount);
expect(tapCount, 0);
// Tap on the track to validate the scroll view will not page.
await tester.tapAt(const Offset(797.0, 200.0));
// Drag in the track area to validate pass through to scrollable.
await tester.dragFrom(
const Offset(797.0, 45.0),
const Offset(0.0, -scrollAmount),
touchSlopY: 0.0,
);
await tester.pumpAndSettle();
// The scroll view received the drag.
expect(scrollController.offset, scrollAmount * 2);
expect(tapCount, 0);
// Tap on the thumb to validate the scroll view receives a click.
await tester.tapAt(const Offset(797.0, 45.0));
await tester.pumpAndSettle();
expect(tapCount, 1);
// Tap on the track to validate the scroll view will not page and receives a click.
await tester.tapAt(const Offset(797.0, 400.0));
await tester.pumpAndSettle();
// The offset should not have changed.
expect(scrollController.offset, scrollAmount);
expect(scrollController.offset, scrollAmount * 2);
expect(tapCount, 2);
});
testWidgets('Simultaneous dragging and pointer scrolling does not cause a crash', (WidgetTester tester) async {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment