mirror of
https://github.com/flutter/flutter
synced 2024-08-25 02:46:31 +00:00
2DScrollView - Fix drag when one axis does not have enough content (#145566)
Fixes https://github.com/flutter/flutter/issues/144982 For reference, may have to do with #138442 when we reworked the gesture handling. The adjustments to the comments here were certainly from #138442 not updating them. Confused myself for a minute or two. ð
This commit is contained in:
parent
14774b95c2
commit
c5047e0ada
|
@ -1982,6 +1982,7 @@ class TwoDimensionalScrollableState extends State<TwoDimensionalScrollable> {
|
||||||
viewportBuilder: (BuildContext context, ViewportOffset verticalOffset) {
|
viewportBuilder: (BuildContext context, ViewportOffset verticalOffset) {
|
||||||
return _HorizontalInnerDimension(
|
return _HorizontalInnerDimension(
|
||||||
key: _horizontalInnerScrollableKey,
|
key: _horizontalInnerScrollableKey,
|
||||||
|
verticalOuterKey: _verticalOuterScrollableKey,
|
||||||
axisDirection: widget.horizontalDetails.direction,
|
axisDirection: widget.horizontalDetails.direction,
|
||||||
controller: widget.horizontalDetails.controller
|
controller: widget.horizontalDetails.controller
|
||||||
?? _horizontalFallbackController!,
|
?? _horizontalFallbackController!,
|
||||||
|
@ -2250,9 +2251,9 @@ class _VerticalOuterDimensionState extends ScrollableState {
|
||||||
if (value) {
|
if (value) {
|
||||||
// Replaces the typical vertical/horizontal drag gesture recognizers
|
// Replaces the typical vertical/horizontal drag gesture recognizers
|
||||||
// with a pan gesture recognizer to allow bidirectional scrolling.
|
// with a pan gesture recognizer to allow bidirectional scrolling.
|
||||||
// Based on the diagonalDragBehavior, valid horizontal deltas are
|
// Based on the diagonalDragBehavior, valid vertical deltas are
|
||||||
// applied to this scrollable, while vertical deltas are routed to
|
// applied to this scrollable, while horizontal deltas are routed to
|
||||||
// the vertical scrollable.
|
// the horizontal scrollable.
|
||||||
_gestureRecognizers = <Type, GestureRecognizerFactory>{
|
_gestureRecognizers = <Type, GestureRecognizerFactory>{
|
||||||
PanGestureRecognizer: GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>(
|
PanGestureRecognizer: GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>(
|
||||||
() => PanGestureRecognizer(supportedDevices: _configuration.dragDevices),
|
() => PanGestureRecognizer(supportedDevices: _configuration.dragDevices),
|
||||||
|
@ -2303,6 +2304,7 @@ class _VerticalOuterDimensionState extends ScrollableState {
|
||||||
class _HorizontalInnerDimension extends Scrollable {
|
class _HorizontalInnerDimension extends Scrollable {
|
||||||
const _HorizontalInnerDimension({
|
const _HorizontalInnerDimension({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.verticalOuterKey,
|
||||||
required super.viewportBuilder,
|
required super.viewportBuilder,
|
||||||
required super.axisDirection,
|
required super.axisDirection,
|
||||||
super.controller,
|
super.controller,
|
||||||
|
@ -2315,6 +2317,7 @@ class _HorizontalInnerDimension extends Scrollable {
|
||||||
this.diagonalDragBehavior = DiagonalDragBehavior.none,
|
this.diagonalDragBehavior = DiagonalDragBehavior.none,
|
||||||
}) : assert(axisDirection == AxisDirection.left || axisDirection == AxisDirection.right);
|
}) : assert(axisDirection == AxisDirection.left || axisDirection == AxisDirection.right);
|
||||||
|
|
||||||
|
final GlobalKey<ScrollableState> verticalOuterKey;
|
||||||
final DiagonalDragBehavior diagonalDragBehavior;
|
final DiagonalDragBehavior diagonalDragBehavior;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -2324,6 +2327,7 @@ class _HorizontalInnerDimension extends Scrollable {
|
||||||
class _HorizontalInnerDimensionState extends ScrollableState {
|
class _HorizontalInnerDimensionState extends ScrollableState {
|
||||||
late ScrollableState verticalScrollable;
|
late ScrollableState verticalScrollable;
|
||||||
|
|
||||||
|
GlobalKey<ScrollableState> get verticalOuterKey => (widget as _HorizontalInnerDimension).verticalOuterKey;
|
||||||
DiagonalDragBehavior get diagonalDragBehavior => (widget as _HorizontalInnerDimension).diagonalDragBehavior;
|
DiagonalDragBehavior get diagonalDragBehavior => (widget as _HorizontalInnerDimension).diagonalDragBehavior;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -2379,9 +2383,12 @@ class _HorizontalInnerDimensionState extends ScrollableState {
|
||||||
case DiagonalDragBehavior.free:
|
case DiagonalDragBehavior.free:
|
||||||
if (value) {
|
if (value) {
|
||||||
// If a type of diagonal scrolling is enabled, a panning gesture
|
// If a type of diagonal scrolling is enabled, a panning gesture
|
||||||
// recognizer will be created for the _InnerDimension. So in this
|
// recognizer will be created for the _VerticalOuterDimension. So in
|
||||||
// case, the _OuterDimension does not require a gesture recognizer.
|
// this case, the _HorizontalInnerDimension does not require a gesture
|
||||||
|
// recognizer, meanwhile we should ensure the outer dimension has
|
||||||
|
// updated in case it did not have enough content to enable dragging.
|
||||||
_gestureRecognizers = const <Type, GestureRecognizerFactory>{};
|
_gestureRecognizers = const <Type, GestureRecognizerFactory>{};
|
||||||
|
verticalOuterKey.currentState!.setCanDrag(value);
|
||||||
// Cancel the active hold/drag (if any) because the gesture recognizers
|
// Cancel the active hold/drag (if any) because the gesture recognizers
|
||||||
// will soon be disposed by our RawGestureDetector, and we won't be
|
// will soon be disposed by our RawGestureDetector, and we won't be
|
||||||
// receiving pointer up events to cancel the hold/drag.
|
// receiving pointer up events to cancel the hold/drag.
|
||||||
|
|
|
@ -580,5 +580,297 @@ void main() {
|
||||||
expect(horizontalController.position.activity!.velocity, 0.0);
|
expect(horizontalController.position.activity!.velocity, 0.0);
|
||||||
expect(verticalController.position.activity!.velocity, 0.0);
|
expect(verticalController.position.activity!.velocity, 0.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('Can drag horizontally when there is not enough vertical content', () {
|
||||||
|
testWidgets('DiagonalDragBehavior.free', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/144982
|
||||||
|
final ScrollController verticalController = ScrollController();
|
||||||
|
addTearDown(verticalController.dispose);
|
||||||
|
final ScrollController horizontalController = ScrollController();
|
||||||
|
addTearDown(horizontalController.dispose);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: SimpleBuilderTableView(
|
||||||
|
verticalDetails: ScrollableDetails.vertical(controller: verticalController),
|
||||||
|
horizontalDetails: ScrollableDetails.horizontal(controller: horizontalController),
|
||||||
|
diagonalDragBehavior: DiagonalDragBehavior.free,
|
||||||
|
delegate: TwoDimensionalChildBuilderDelegate(
|
||||||
|
maxXIndex: 20,
|
||||||
|
maxYIndex: 1,
|
||||||
|
builder: _testChildBuilder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
expect(verticalController.position.maxScrollExtent, 0.0);
|
||||||
|
expect(horizontalController.position.maxScrollExtent, 3400.0);
|
||||||
|
// Fling vertically, nothing should happen.
|
||||||
|
await tester.fling(
|
||||||
|
find.byType(TwoDimensionalScrollable),
|
||||||
|
const Offset(0.0, -200.0),
|
||||||
|
2000.0,
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
// Fling horizontally, the horizontal position should change.
|
||||||
|
await tester.fling(
|
||||||
|
find.byType(TwoDimensionalScrollable),
|
||||||
|
const Offset(-200.0, 0.0),
|
||||||
|
2000.0,
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, greaterThan(840.0));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('DiagonalDragBehavior.weightedEvent', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/144982
|
||||||
|
final ScrollController verticalController = ScrollController();
|
||||||
|
addTearDown(verticalController.dispose);
|
||||||
|
final ScrollController horizontalController = ScrollController();
|
||||||
|
addTearDown(horizontalController.dispose);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: SimpleBuilderTableView(
|
||||||
|
verticalDetails: ScrollableDetails.vertical(controller: verticalController),
|
||||||
|
horizontalDetails: ScrollableDetails.horizontal(controller: horizontalController),
|
||||||
|
diagonalDragBehavior: DiagonalDragBehavior.weightedEvent,
|
||||||
|
delegate: TwoDimensionalChildBuilderDelegate(
|
||||||
|
maxXIndex: 20,
|
||||||
|
maxYIndex: 1,
|
||||||
|
builder: _testChildBuilder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
expect(verticalController.position.maxScrollExtent, 0.0);
|
||||||
|
expect(horizontalController.position.maxScrollExtent, 3400.0);
|
||||||
|
// Fling vertically, nothing should happen.
|
||||||
|
await tester.fling(
|
||||||
|
find.byType(TwoDimensionalScrollable),
|
||||||
|
const Offset(0.0, -200.0),
|
||||||
|
2000.0,
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
// Fling horizontally, the horizontal position should change.
|
||||||
|
await tester.fling(
|
||||||
|
find.byType(TwoDimensionalScrollable),
|
||||||
|
const Offset(-200.0, 0.0),
|
||||||
|
2000.0,
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, greaterThan(840.0));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('DiagonalDragBehavior.weightedContinuous', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/144982
|
||||||
|
final ScrollController verticalController = ScrollController();
|
||||||
|
addTearDown(verticalController.dispose);
|
||||||
|
final ScrollController horizontalController = ScrollController();
|
||||||
|
addTearDown(horizontalController.dispose);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: SimpleBuilderTableView(
|
||||||
|
verticalDetails: ScrollableDetails.vertical(controller: verticalController),
|
||||||
|
horizontalDetails: ScrollableDetails.horizontal(controller: horizontalController),
|
||||||
|
diagonalDragBehavior: DiagonalDragBehavior.weightedContinuous,
|
||||||
|
delegate: TwoDimensionalChildBuilderDelegate(
|
||||||
|
maxXIndex: 20,
|
||||||
|
maxYIndex: 1,
|
||||||
|
builder: _testChildBuilder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
expect(verticalController.position.maxScrollExtent, 0.0);
|
||||||
|
expect(horizontalController.position.maxScrollExtent, 3400.0);
|
||||||
|
// Fling vertically, nothing should happen.
|
||||||
|
await tester.fling(
|
||||||
|
find.byType(TwoDimensionalScrollable),
|
||||||
|
const Offset(0.0, -200.0),
|
||||||
|
2000.0,
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
// Fling horizontally, the horizontal position should change.
|
||||||
|
await tester.fling(
|
||||||
|
find.byType(TwoDimensionalScrollable),
|
||||||
|
const Offset(-200.0, 0.0),
|
||||||
|
2000.0,
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, greaterThan(840.0));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('Can drag vertically when there is not enough horizontal content', () {
|
||||||
|
testWidgets('DiagonalDragBehavior.free', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/144982
|
||||||
|
final ScrollController verticalController = ScrollController();
|
||||||
|
addTearDown(verticalController.dispose);
|
||||||
|
final ScrollController horizontalController = ScrollController();
|
||||||
|
addTearDown(horizontalController.dispose);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: SimpleBuilderTableView(
|
||||||
|
verticalDetails: ScrollableDetails.vertical(controller: verticalController),
|
||||||
|
horizontalDetails: ScrollableDetails.horizontal(controller: horizontalController),
|
||||||
|
diagonalDragBehavior: DiagonalDragBehavior.free,
|
||||||
|
delegate: TwoDimensionalChildBuilderDelegate(
|
||||||
|
maxXIndex: 1,
|
||||||
|
maxYIndex: 20,
|
||||||
|
builder: _testChildBuilder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
expect(verticalController.position.maxScrollExtent, 3600.0);
|
||||||
|
expect(horizontalController.position.maxScrollExtent, 0.0);
|
||||||
|
// Fling horizontally, nothing should happen.
|
||||||
|
await tester.fling(
|
||||||
|
find.byType(TwoDimensionalScrollable),
|
||||||
|
const Offset(-200.0, 0.0),
|
||||||
|
2000.0,
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
// Fling vertically, the vertical position should change.
|
||||||
|
await tester.fling(
|
||||||
|
find.byType(TwoDimensionalScrollable),
|
||||||
|
const Offset(0.0, -200.0),
|
||||||
|
2000.0,
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, greaterThan(840.0));
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('DiagonalDragBehavior.weightedEvent', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/144982
|
||||||
|
final ScrollController verticalController = ScrollController();
|
||||||
|
addTearDown(verticalController.dispose);
|
||||||
|
final ScrollController horizontalController = ScrollController();
|
||||||
|
addTearDown(horizontalController.dispose);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: SimpleBuilderTableView(
|
||||||
|
verticalDetails: ScrollableDetails.vertical(controller: verticalController),
|
||||||
|
horizontalDetails: ScrollableDetails.horizontal(controller: horizontalController),
|
||||||
|
diagonalDragBehavior: DiagonalDragBehavior.weightedEvent,
|
||||||
|
delegate: TwoDimensionalChildBuilderDelegate(
|
||||||
|
maxXIndex: 1,
|
||||||
|
maxYIndex: 20,
|
||||||
|
builder: _testChildBuilder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
expect(verticalController.position.maxScrollExtent, 3600.0);
|
||||||
|
expect(horizontalController.position.maxScrollExtent, 0.0);
|
||||||
|
// Fling horizontally, nothing should happen.
|
||||||
|
await tester.fling(
|
||||||
|
find.byType(TwoDimensionalScrollable),
|
||||||
|
const Offset(-200.0, 0.0),
|
||||||
|
2000.0,
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
// Fling vertically, the vertical position should change.
|
||||||
|
await tester.fling(
|
||||||
|
find.byType(TwoDimensionalScrollable),
|
||||||
|
const Offset(0.0, -200.0),
|
||||||
|
2000.0,
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, greaterThan(840.0));
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('DiagonalDragBehavior.weightedContinuous', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/144982
|
||||||
|
final ScrollController verticalController = ScrollController();
|
||||||
|
addTearDown(verticalController.dispose);
|
||||||
|
final ScrollController horizontalController = ScrollController();
|
||||||
|
addTearDown(horizontalController.dispose);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: SimpleBuilderTableView(
|
||||||
|
verticalDetails: ScrollableDetails.vertical(controller: verticalController),
|
||||||
|
horizontalDetails: ScrollableDetails.horizontal(controller: horizontalController),
|
||||||
|
diagonalDragBehavior: DiagonalDragBehavior.weightedContinuous,
|
||||||
|
delegate: TwoDimensionalChildBuilderDelegate(
|
||||||
|
maxXIndex: 1,
|
||||||
|
maxYIndex: 20,
|
||||||
|
builder: _testChildBuilder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
expect(verticalController.position.maxScrollExtent, 3600.0);
|
||||||
|
expect(horizontalController.position.maxScrollExtent, 0.0);
|
||||||
|
// Fling horizontally, nothing should happen.
|
||||||
|
await tester.fling(
|
||||||
|
find.byType(TwoDimensionalScrollable),
|
||||||
|
const Offset(-200.0, 0.0),
|
||||||
|
2000.0,
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, 0.0);
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
// Fling vertically, the vertical position should change.
|
||||||
|
await tester.fling(
|
||||||
|
find.byType(TwoDimensionalScrollable),
|
||||||
|
const Offset(0.0, -200.0),
|
||||||
|
2000.0,
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(verticalController.position.pixels, greaterThan(840.0));
|
||||||
|
expect(horizontalController.position.pixels, 0.0);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue