diff --git a/packages/flutter/lib/src/widgets/scrollbar.dart b/packages/flutter/lib/src/widgets/scrollbar.dart index a9240f6de5ae..11b59dba066c 100644 --- a/packages/flutter/lib/src/widgets/scrollbar.dart +++ b/packages/flutter/lib/src/widgets/scrollbar.dart @@ -1953,9 +1953,17 @@ class RawScrollbarState extends State with TickerProv instance.dragStartBehavior = DragStartBehavior.down; } + bool _canHandleScrollGestures() { + return enableGestures + && _effectiveScrollController != null + && _effectiveScrollController!.positions.length == 1 + && _effectiveScrollController!.position.hasContentDimensions + && _effectiveScrollController!.position.maxScrollExtent > 0.0; + } + Map get _gestures { final Map gestures = {}; - if (_effectiveScrollController == null || !enableGestures) { + if (!_canHandleScrollGestures()) { return gestures; } diff --git a/packages/flutter/test/widgets/scrollbar_test.dart b/packages/flutter/test/widgets/scrollbar_test.dart index be62c9ca35da..5c1ae5bd3a02 100644 --- a/packages/flutter/test/widgets/scrollbar_test.dart +++ b/packages/flutter/test/widgets/scrollbar_test.dart @@ -5,6 +5,7 @@ import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/src/physics/utils.dart' show nearEqual; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -3212,4 +3213,47 @@ The provided ScrollController cannot be shared by multiple ScrollView widgets.'' }, variant: TargetPlatformVariant.all(), ); + + testWidgets('Safe to drag trackpad when maxScrollExtent is 0 (scrollbar is not painted)', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/149803 + final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); + + Widget buildFrame(double sizedBoxHeight) { + return Directionality( + textDirection: TextDirection.ltr, + child: MediaQuery( + data: const MediaQueryData(), + child: RawScrollbar( + controller: scrollController, + child: SingleChildScrollView( + controller: scrollController, + child: SizedBox(width: 100.0, height: sizedBoxHeight), + ), + ), + ), + ); + } + + await tester.pumpWidget(buildFrame(100)); // Test viewport has height=600 + await tester.pumpAndSettle(); + expect(scrollController.offset, 0.0); + expect(scrollController.position.maxScrollExtent, 0.0); + + await tester.drag(find.byType(SingleChildScrollView), const Offset(0, -100), kind: PointerDeviceKind.trackpad); + await tester.pumpAndSettle(); + expect(scrollController.offset, 0.0); + expect(scrollController.position.maxScrollExtent, 0.0); + + await tester.pumpWidget(buildFrame(700)); + await tester.pumpAndSettle(); + expect(scrollController.offset, 0.0); + expect(scrollController.position.maxScrollExtent, 100.0); + + await tester.drag(find.byType(SingleChildScrollView), const Offset(0, -100), kind: PointerDeviceKind.trackpad); + await tester.pumpAndSettle(); + expect(scrollController.offset, 100.0); + expect(scrollController.position.maxScrollExtent, 100.0); + + }); }