Fix pointer scroll for nested NeverScrollables (#70953)
This commit is contained in:
parent
974661924b
commit
63e328f7f0
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
@ -628,9 +629,17 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
|
||||
|
||||
// SCROLL WHEEL
|
||||
|
||||
// Returns the offset that should result from applying [event] to the current
|
||||
// position, taking min/max scroll extent into account.
|
||||
double _targetScrollOffsetForPointerScroll(PointerScrollEvent event) {
|
||||
final double delta = _pointerSignalEventDelta(event);
|
||||
return math.min(math.max(position.pixels + delta, position.minScrollExtent),
|
||||
position.maxScrollExtent);
|
||||
}
|
||||
|
||||
// Returns the delta that should result from applying [event] with axis and
|
||||
// direction taken into account.
|
||||
double _targetScrollDeltaForPointerScroll(PointerScrollEvent event) {
|
||||
double _pointerSignalEventDelta(PointerScrollEvent event) {
|
||||
double delta = widget.axis == Axis.horizontal
|
||||
? event.scrollDelta.dx
|
||||
: event.scrollDelta.dy;
|
||||
@ -638,15 +647,17 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
|
||||
if (axisDirectionIsReversed(widget.axisDirection)) {
|
||||
delta *= -1;
|
||||
}
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
void _receivedPointerSignal(PointerSignalEvent event) {
|
||||
if (event is PointerScrollEvent && _position != null) {
|
||||
final double targetScrollOffset = _targetScrollDeltaForPointerScroll(event);
|
||||
if (_physics != null && !_physics!.shouldAcceptUserOffset(position)) {
|
||||
return;
|
||||
}
|
||||
final double targetScrollOffset = _targetScrollOffsetForPointerScroll(event);
|
||||
// Only express interest in the event if it would actually result in a scroll.
|
||||
if (targetScrollOffset != 0) {
|
||||
if (targetScrollOffset != position.pixels) {
|
||||
GestureBinding.instance!.pointerSignalResolver.register(event, _handlePointerScroll);
|
||||
}
|
||||
}
|
||||
@ -654,12 +665,9 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
|
||||
|
||||
void _handlePointerScroll(PointerEvent event) {
|
||||
assert(event is PointerScrollEvent);
|
||||
if (_physics != null && !_physics!.shouldAcceptUserOffset(position)) {
|
||||
return;
|
||||
}
|
||||
final double targetScrollOffset = _targetScrollDeltaForPointerScroll(event as PointerScrollEvent);
|
||||
if (targetScrollOffset != 0) {
|
||||
position.pointerScroll(targetScrollOffset);
|
||||
final double targetScrollOffset = _targetScrollOffsetForPointerScroll(event as PointerScrollEvent);
|
||||
if (targetScrollOffset != position.pixels) {
|
||||
position.pointerScroll(_pointerSignalEventDelta(event));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1148,6 +1148,64 @@ void main() {
|
||||
expect(targetMidRightPage1, findsOneWidget);
|
||||
expect(targetMidLeftPage1, findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('PointerScroll on nested NeverScrollable ListView goes to outer Scrollable.', (WidgetTester tester) async {
|
||||
// Regression test for https://github.com/flutter/flutter/issues/70948
|
||||
final ScrollController outerController = ScrollController();
|
||||
final ScrollController innerController = ScrollController();
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
controller: outerController,
|
||||
child: Container(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
children: <Widget>[
|
||||
for (int i = 0; i < 100; i++)
|
||||
Text('SingleChildScrollView $i'),
|
||||
]
|
||||
),
|
||||
Container(
|
||||
height: 3000,
|
||||
width: 400,
|
||||
child: ListView.builder(
|
||||
controller: innerController,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: 100,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return Text('Nested NeverScrollable ListView $index');
|
||||
},
|
||||
)
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
));
|
||||
expect(outerController.position.pixels, 0.0);
|
||||
expect(innerController.position.pixels, 0.0);
|
||||
final Offset outerScrollable = tester.getCenter(find.text('SingleChildScrollView 3'));
|
||||
final TestPointer testPointer = TestPointer(1, ui.PointerDeviceKind.mouse);
|
||||
// Hover over the outer scroll view and create a pointer scroll.
|
||||
testPointer.hover(outerScrollable);
|
||||
await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, 20.0)));
|
||||
await tester.pump(const Duration(milliseconds: 250));
|
||||
expect(outerController.position.pixels, 20.0);
|
||||
expect(innerController.position.pixels, 0.0);
|
||||
|
||||
final Offset innerScrollable = tester.getCenter(find.text('Nested NeverScrollable ListView 20'));
|
||||
// Hover over the inner scroll view and create a pointer scroll.
|
||||
// This inner scroll view is not scrollable, and so the outer should scroll.
|
||||
testPointer.hover(innerScrollable);
|
||||
await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, -20.0)));
|
||||
await tester.pump(const Duration(milliseconds: 250));
|
||||
expect(outerController.position.pixels, 0.0);
|
||||
expect(innerController.position.pixels, 0.0);
|
||||
});
|
||||
}
|
||||
|
||||
// ignore: must_be_immutable
|
||||
|
Loading…
x
Reference in New Issue
Block a user