diff --git a/packages/flutter/lib/src/widgets/scrollable.dart b/packages/flutter/lib/src/widgets/scrollable.dart index 42b91a9747..de3a9a0795 100644 --- a/packages/flutter/lib/src/widgets/scrollable.dart +++ b/packages/flutter/lib/src/widgets/scrollable.dart @@ -524,21 +524,29 @@ class ScrollableState extends State with TickerProviderStateMixin @override Widget build(BuildContext context) { assert(position != null); - // TODO(ianh): Having all these global keys is sad. - Widget result = RawGestureDetector( - key: _gestureDetectorKey, - gestures: _gestureRecognizers, - behavior: HitTestBehavior.opaque, - excludeFromSemantics: widget.excludeFromSemantics, - child: Semantics( - explicitChildNodes: !widget.excludeFromSemantics, - child: IgnorePointer( - key: _ignorePointerKey, - ignoring: _shouldIgnorePointer, - ignoringSemantics: false, - child: _ScrollableScope( - scrollable: this, - position: position, + // _ScrollableScope must be placed above the BuildContext returned by notificationContext + // so that we can get this ScrollableState by doing the following: + // + // ScrollNotification notification; + // Scrollable.of(notification.context) + // + // Since notificationContext is pointing to _gestureDetectorKey.context, _ScrollableScope + // must be placed above the widget using it: RawGestureDetector + Widget result = _ScrollableScope( + scrollable: this, + position: position, + // TODO(ianh): Having all these global keys is sad. + child: RawGestureDetector( + key: _gestureDetectorKey, + gestures: _gestureRecognizers, + behavior: HitTestBehavior.opaque, + excludeFromSemantics: widget.excludeFromSemantics, + child: Semantics( + explicitChildNodes: !widget.excludeFromSemantics, + child: IgnorePointer( + key: _ignorePointerKey, + ignoring: _shouldIgnorePointer, + ignoringSemantics: false, child: widget.viewportBuilder(context, position), ), ), diff --git a/packages/flutter/test/widgets/scrollable_of_test.dart b/packages/flutter/test/widgets/scrollable_of_test.dart index a91ac4b19d..291a8213d5 100644 --- a/packages/flutter/test/widgets/scrollable_of_test.dart +++ b/packages/flutter/test/widgets/scrollable_of_test.dart @@ -82,4 +82,24 @@ void main() { controller.jumpTo(400.0); expect(logValue, 'listener 400.0'); }); + + testWidgets('Scrollable.of() is possible using ScrollNotification context', (WidgetTester tester) async { + ScrollNotification notification; + + await tester.pumpWidget(NotificationListener( + onNotification: (ScrollNotification value) { + notification = value; + return false; + }, + child: SingleChildScrollView( + child: const SizedBox(height: 1200.0) + ) + )); + + await tester.startGesture(const Offset(100.0, 100.0)); + await tester.pump(const Duration(seconds: 1)); + + final StatefulElement scrollableElement = find.byType(Scrollable).evaluate().first; + expect(Scrollable.of(notification.context), equals(scrollableElement.state)); + }); }