From 59f7e7f08e836235ddefd731220ed7ee7e4a7784 Mon Sep 17 00:00:00 2001 From: Hixie Date: Tue, 22 Sep 2015 09:50:48 -0700 Subject: [PATCH] Fix scrolling of Block. Since our build function depends on scrollBehavior.isScrollable, any time we update scrollBehavior we are implicitly updating our state. As such, we must do so during a setState() call, or else we won't rebuild and might not bother to listen to the scroll gestures. This probably broke when we made Block not listen to gestures if it wasn't overflowing. --- .../flutter/lib/src/widgets/scrollable.dart | 33 +++++++--- packages/unit/test/widget/block_test.dart | 64 +++++++++++++++++++ 2 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 packages/unit/test/widget/block_test.dart diff --git a/packages/flutter/lib/src/widgets/scrollable.dart b/packages/flutter/lib/src/widgets/scrollable.dart index 933af0f7e4..a5e7460435 100644 --- a/packages/flutter/lib/src/widgets/scrollable.dart +++ b/packages/flutter/lib/src/widgets/scrollable.dart @@ -277,17 +277,23 @@ class ScrollableViewport extends Scrollable { double _childSize = 0.0; void _handleViewportSizeChanged(Size newSize) { _viewportSize = scrollDirection == ScrollDirection.vertical ? newSize.height : newSize.width; - _updateScrollBehaviour(); + setState(() { + _updateScrollBehaviour(); + }); } void _handleChildSizeChanged(Size newSize) { _childSize = scrollDirection == ScrollDirection.vertical ? newSize.height : newSize.width; - _updateScrollBehaviour(); + setState(() { + _updateScrollBehaviour(); + }); } void _updateScrollBehaviour() { + // if you don't call this from build() or syncConstructorArguments(), you must call it from setState(). scrollTo(scrollBehavior.updateExtents( contentExtent: _childSize, containerExtent: _viewportSize, - scrollOffset: scrollOffset)); + scrollOffset: scrollOffset + )); } Widget buildContent() { @@ -423,6 +429,7 @@ abstract class ScrollableWidgetList extends Scrollable { } void _updateScrollBehavior() { + // if you don't call this from build() or syncConstructorArguments(), you must call it from setState(). double contentExtent = itemExtent * itemCount; if (padding != null) contentExtent += _leadingPadding + _trailingPadding; @@ -636,16 +643,22 @@ class ScrollableMixedWidgetList extends Scrollable { OverscrollBehavior get scrollBehavior => super.scrollBehavior; void _handleSizeChanged(Size newSize) { - scrollBy(scrollBehavior.updateExtents( - containerExtent: newSize.height, - scrollOffset: scrollOffset - )); + setState(() { + scrollBy(scrollBehavior.updateExtents( + containerExtent: newSize.height, + scrollOffset: scrollOffset + )); + }); } void _handleLayoutChanged() { - double newScrollOffset = scrollBehavior.updateExtents( - contentExtent: layoutState.didReachLastChild ? layoutState.contentsSize : double.INFINITY, - scrollOffset: scrollOffset); + double newScrollOffset; + setState(() { + newScrollOffset = scrollBehavior.updateExtents( + contentExtent: layoutState.didReachLastChild ? layoutState.contentsSize : double.INFINITY, + scrollOffset: scrollOffset + ); + }); if (_contentChanged) { _contentChanged = false; scrollTo(newScrollOffset); diff --git a/packages/unit/test/widget/block_test.dart b/packages/unit/test/widget/block_test.dart new file mode 100644 index 0000000000..217c21282f --- /dev/null +++ b/packages/unit/test/widget/block_test.dart @@ -0,0 +1,64 @@ +import 'package:quiver/testing/async.dart'; +import 'package:sky/widgets.dart'; +import 'package:test/test.dart'; + +import '../engine/mock_events.dart'; +import 'widget_tester.dart'; + +final Key blockKey = new Key('test'); + +void main() { + test('Cannot scroll a non-overflowing block', () { + WidgetTester tester = new WidgetTester(); + + tester.pumpFrame(() { + return new Block([ + new Container( + height: 200.0, // less than 600, the height of the test area + child: new Text('Hello') + ) + ], + key: blockKey); + }); + tester.pumpFrameWithoutChange(); // for SizeObservers + + Point middleOfContainer = tester.getCenter(tester.findText('Hello')); + Point target = tester.getCenter(tester.findWidget((widget) => widget.key == blockKey)); + TestPointer pointer = new TestPointer(); + tester.dispatchEvent(pointer.down(target), target); + tester.dispatchEvent(pointer.move(target + const Offset(0.0, -10.0)), target); + + tester.pumpFrameWithoutChange(1.0); + + expect(tester.getCenter(tester.findText('Hello')) == middleOfContainer, isTrue); + + tester.dispatchEvent(pointer.up(), target); + }); + + test('Can scroll an overflowing block', () { + WidgetTester tester = new WidgetTester(); + + tester.pumpFrame(() { + return new Block([ + new Container( + height: 2000.0, // more than 600, the height of the test area + child: new Text('Hello') + ) + ], + key: blockKey); + }); + tester.pumpFrameWithoutChange(); // for SizeObservers + + Point middleOfContainer = tester.getCenter(tester.findText('Hello')); + Point target = tester.getCenter(tester.findWidget((widget) => widget.key == blockKey)); + TestPointer pointer = new TestPointer(); + tester.dispatchEvent(pointer.down(target), target); + tester.dispatchEvent(pointer.move(target + const Offset(0.0, -10.0)), target); + + tester.pumpFrameWithoutChange(1.0); + + expect(tester.getCenter(tester.findText('Hello')) == middleOfContainer, isFalse); + + tester.dispatchEvent(pointer.up(), target); + }); +}