Commit f8080557 authored by Hixie's avatar Hixie

Remove size observers from scrollables.

Also:
 - add operator==/hashCode/toString to ViewportDimensions
 - add toString to BindingBase
 - add toString and debugFillDescription to ScrollBehavior
 - fix a bug in the RawGestureDetectorState's replaceGestureRecognizers
 - rename MixedViewport's onExtentsUpdate to onExtentChanged
 - replace ExtentsUpdateCallback with ValueChanged<double>
 - remove a microtask for dispatching scroll start, since it
   did not appear to have any purpose
 - added dartdocs to Instrumentation until I understood it
 - made all event dispatch in Instrumentation drain microtasks
parent 1ce3146d
...@@ -699,10 +699,10 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect ...@@ -699,10 +699,10 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
} }
void _updateScrollBehavior() { void _updateScrollBehavior() {
scrollBehavior.updateExtents( scrollTo(scrollBehavior.updateExtents(
containerExtent: config.scrollDirection == Axis.vertical ? _viewportSize.height : _viewportSize.width, containerExtent: config.scrollDirection == Axis.vertical ? _viewportSize.height : _viewportSize.width,
contentExtent: _tabWidths.reduce((double sum, double width) => sum + width) contentExtent: _tabWidths.reduce((double sum, double width) => sum + width)
); ));
} }
void _layoutChanged(Size tabBarSize, List<double> tabWidths) { void _layoutChanged(Size tabBarSize, List<double> tabWidths) {
...@@ -713,11 +713,16 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect ...@@ -713,11 +713,16 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
}); });
} }
void _handleViewportSizeChanged(Size newSize) { Offset _handlePaintOffsetUpdateNeeded(ViewportDimensions dimensions) {
_viewportSize = newSize; // We make various state changes here but don't have to do so in a
// setState() callback because we are called during layout and all
// we're updating is the new offset, which we are providing to the
// render object via our return value.
_viewportSize = dimensions.containerSize;
_updateScrollBehavior(); _updateScrollBehavior();
if (config.isScrollable) if (config.isScrollable)
scrollTo(_centeredTabScrollOffset(_selection.index), duration: _kTabBarScroll); scrollTo(_centeredTabScrollOffset(_selection.index), duration: _kTabBarScroll);
return scrollOffsetToPixelDelta(scrollOffset);
} }
Widget buildContent(BuildContext context) { Widget buildContent(BuildContext context) {
...@@ -772,13 +777,11 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect ...@@ -772,13 +777,11 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
); );
if (config.isScrollable) { if (config.isScrollable) {
contents = new SizeObserver( child: new Viewport(
onSizeChanged: _handleViewportSizeChanged, scrollDirection: Axis.horizontal,
child: new Viewport( paintOffset: scrollOffsetToPixelDelta(scrollOffset),
scrollDirection: Axis.horizontal, onPaintOffsetUpdateNeeded: _handlePaintOffsetUpdateNeeded,
paintOffset: scrollOffsetToPixelDelta(scrollOffset), child: contents
child: contents
)
); );
} }
......
...@@ -39,6 +39,20 @@ class ViewportDimensions { ...@@ -39,6 +39,20 @@ class ViewportDimensions {
return paintOffset + (containerSize - contentSize); return paintOffset + (containerSize - contentSize);
} }
} }
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other is! ViewportDimensions)
return false;
final ViewportDimensions typedOther = other;
return contentSize == typedOther.contentSize &&
containerSize == typedOther.containerSize;
}
int get hashCode => hashValues(contentSize, containerSize);
String toString() => 'ViewportDimensions(container: $containerSize, content: $contentSize)';
} }
abstract class HasScrollDirection { abstract class HasScrollDirection {
...@@ -163,6 +177,8 @@ class RenderViewportBase extends RenderBox implements HasScrollDirection { ...@@ -163,6 +177,8 @@ class RenderViewportBase extends RenderBox implements HasScrollDirection {
} }
typedef Offset ViewportDimensionsChangeCallback(ViewportDimensions dimensions);
/// A render object that's bigger on the inside. /// A render object that's bigger on the inside.
/// ///
/// The child of a viewport can layout to a larger size than the viewport /// The child of a viewport can layout to a larger size than the viewport
...@@ -176,11 +192,16 @@ class RenderViewport extends RenderViewportBase with RenderObjectWithChildMixin< ...@@ -176,11 +192,16 @@ class RenderViewport extends RenderViewportBase with RenderObjectWithChildMixin<
Offset paintOffset: Offset.zero, Offset paintOffset: Offset.zero,
Axis scrollDirection: Axis.vertical, Axis scrollDirection: Axis.vertical,
ViewportAnchor scrollAnchor: ViewportAnchor.start, ViewportAnchor scrollAnchor: ViewportAnchor.start,
Painter overlayPainter Painter overlayPainter,
this.onPaintOffsetUpdateNeeded
}) : super(paintOffset, scrollDirection, scrollAnchor, overlayPainter) { }) : super(paintOffset, scrollDirection, scrollAnchor, overlayPainter) {
this.child = child; this.child = child;
} }
/// Called during [layout] to report the dimensions of the viewport
/// and its child.
ViewportDimensionsChangeCallback onPaintOffsetUpdateNeeded;
BoxConstraints _getInnerConstraints(BoxConstraints constraints) { BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
BoxConstraints innerConstraints; BoxConstraints innerConstraints;
switch (scrollDirection) { switch (scrollDirection) {
...@@ -228,6 +249,7 @@ class RenderViewport extends RenderViewportBase with RenderObjectWithChildMixin< ...@@ -228,6 +249,7 @@ class RenderViewport extends RenderViewportBase with RenderObjectWithChildMixin<
// parent was baseline-aligned, which makes no sense. // parent was baseline-aligned, which makes no sense.
void performLayout() { void performLayout() {
ViewportDimensions oldDimensions = dimensions;
if (child != null) { if (child != null) {
child.layout(_getInnerConstraints(constraints), parentUsesSize: true); child.layout(_getInnerConstraints(constraints), parentUsesSize: true);
size = constraints.constrain(child.size); size = constraints.constrain(child.size);
...@@ -238,6 +260,9 @@ class RenderViewport extends RenderViewportBase with RenderObjectWithChildMixin< ...@@ -238,6 +260,9 @@ class RenderViewport extends RenderViewportBase with RenderObjectWithChildMixin<
performResize(); performResize();
dimensions = new ViewportDimensions(containerSize: size); dimensions = new ViewportDimensions(containerSize: size);
} }
if (onPaintOffsetUpdateNeeded != null && dimensions != oldDimensions)
paintOffset = onPaintOffsetUpdateNeeded(dimensions);
assert(paintOffset != null);
} }
bool _shouldClipAtPaintOffset(Offset paintOffset) { bool _shouldClipAtPaintOffset(Offset paintOffset) {
......
...@@ -37,6 +37,8 @@ abstract class BindingBase { ...@@ -37,6 +37,8 @@ abstract class BindingBase {
void initInstances() { void initInstances() {
assert(() { _debugInitialized = true; return true; }); assert(() { _debugInitialized = true; return true; });
} }
String toString() => '<$runtimeType>';
} }
// A replacement for shell.connectToService. Implementations should return true // A replacement for shell.connectToService. Implementations should return true
......
...@@ -43,7 +43,9 @@ export 'package:flutter/rendering.dart' show ...@@ -43,7 +43,9 @@ export 'package:flutter/rendering.dart' show
RelativeRect, RelativeRect,
ShaderCallback, ShaderCallback,
ValueChanged, ValueChanged,
ViewportAnchor; ViewportAnchor,
ViewportDimensions,
ViewportDimensionsChangeCallback;
// PAINTING NODES // PAINTING NODES
...@@ -777,6 +779,7 @@ class Viewport extends OneChildRenderObjectWidget { ...@@ -777,6 +779,7 @@ class Viewport extends OneChildRenderObjectWidget {
this.scrollDirection: Axis.vertical, this.scrollDirection: Axis.vertical,
this.scrollAnchor: ViewportAnchor.start, this.scrollAnchor: ViewportAnchor.start,
this.overlayPainter, this.overlayPainter,
this.onPaintOffsetUpdateNeeded,
Widget child Widget child
}) : super(key: key, child: child) { }) : super(key: key, child: child) {
assert(scrollDirection != null); assert(scrollDirection != null);
...@@ -802,11 +805,14 @@ class Viewport extends OneChildRenderObjectWidget { ...@@ -802,11 +805,14 @@ class Viewport extends OneChildRenderObjectWidget {
/// Often used to paint scroll bars. /// Often used to paint scroll bars.
final Painter overlayPainter; final Painter overlayPainter;
final ViewportDimensionsChangeCallback onPaintOffsetUpdateNeeded;
RenderViewport createRenderObject() { RenderViewport createRenderObject() {
return new RenderViewport( return new RenderViewport(
paintOffset: paintOffset, paintOffset: paintOffset,
scrollDirection: scrollDirection, scrollDirection: scrollDirection,
scrollAnchor: scrollAnchor, scrollAnchor: scrollAnchor,
onPaintOffsetUpdateNeeded: onPaintOffsetUpdateNeeded,
overlayPainter: overlayPainter overlayPainter: overlayPainter
); );
} }
...@@ -817,6 +823,7 @@ class Viewport extends OneChildRenderObjectWidget { ...@@ -817,6 +823,7 @@ class Viewport extends OneChildRenderObjectWidget {
..scrollDirection = scrollDirection ..scrollDirection = scrollDirection
..scrollAnchor = scrollAnchor ..scrollAnchor = scrollAnchor
..paintOffset = paintOffset ..paintOffset = paintOffset
..onPaintOffsetUpdateNeeded = onPaintOffsetUpdateNeeded
..overlayPainter = overlayPainter; ..overlayPainter = overlayPainter;
} }
} }
......
...@@ -280,29 +280,29 @@ class RawGestureDetectorState extends State<RawGestureDetector> { ...@@ -280,29 +280,29 @@ class RawGestureDetectorState extends State<RawGestureDetector> {
void replaceGestureRecognizers(Map<Type, GestureRecognizerFactory> gestures) { void replaceGestureRecognizers(Map<Type, GestureRecognizerFactory> gestures) {
assert(() { assert(() {
RenderObject renderObject = context.findRenderObject(); RenderObject renderObject = context.findRenderObject();
assert(renderObject is RenderPointerListener);
RenderPointerListener listener = renderObject;
RenderBox descendant = listener.child;
if (!config.excludeFromSemantics) { if (!config.excludeFromSemantics) {
assert(descendant is RenderSemanticsGestureHandler); assert(renderObject is RenderSemanticsGestureHandler);
RenderSemanticsGestureHandler semanticsGestureHandler = descendant; RenderSemanticsGestureHandler semanticsGestureHandler = renderObject;
descendant = semanticsGestureHandler.child; renderObject = semanticsGestureHandler.child;
} }
assert(descendant != null); assert(renderObject is RenderPointerListener);
if (!descendant.debugDoingThisLayout) { RenderPointerListener pointerListener = renderObject;
renderObject = pointerListener.child;
if (!renderObject.debugDoingThisLayout) {
throw new WidgetError( throw new WidgetError(
'replaceGestureRecognizers() can only be called during the layout phase of the GestureDetector\'s nearest descendant RenderObjectWidget.\n' 'replaceGestureRecognizers() can only be called during the layout phase of the GestureDetector\'s nearest descendant RenderObjectWidget.\n'
'In this particular case, that is:\n' 'In this particular case, that is:\n'
' $descendant' ' $renderObject'
); );
} }
return true; return true;
}); });
_syncAll(gestures); _syncAll(gestures);
if (!config.excludeFromSemantics) { if (!config.excludeFromSemantics) {
RenderPointerListener listener = context.findRenderObject(); RenderSemanticsGestureHandler semanticsGestureHandler = context.findRenderObject();
RenderSemanticsGestureHandler semanticsGestureHandler = listener.child; context.visitChildElements((RenderObjectElement element) {
context.visitChildElements((RenderObjectElement element) => element.widget.updateRenderObject(semanticsGestureHandler, null)); element.widget.updateRenderObject(semanticsGestureHandler, null);
});
} }
} }
......
...@@ -10,7 +10,6 @@ import 'framework.dart'; ...@@ -10,7 +10,6 @@ import 'framework.dart';
import 'basic.dart'; import 'basic.dart';
typedef Widget IndexedBuilder(BuildContext context, int index); // return null if index is greater than index of last entry typedef Widget IndexedBuilder(BuildContext context, int index); // return null if index is greater than index of last entry
typedef void ExtentsUpdateCallback(double newExtents);
typedef void InvalidatorCallback(Iterable<int> indices); typedef void InvalidatorCallback(Iterable<int> indices);
typedef void InvalidatorAvailableCallback(InvalidatorCallback invalidator); typedef void InvalidatorAvailableCallback(InvalidatorCallback invalidator);
...@@ -23,7 +22,7 @@ class MixedViewport extends RenderObjectWidget { ...@@ -23,7 +22,7 @@ class MixedViewport extends RenderObjectWidget {
this.direction: Axis.vertical, this.direction: Axis.vertical,
this.builder, this.builder,
this.token, this.token,
this.onExtentsUpdate, this.onExtentChanged,
this.onInvalidatorAvailable this.onInvalidatorAvailable
}) : super(key: key); }) : super(key: key);
...@@ -31,7 +30,7 @@ class MixedViewport extends RenderObjectWidget { ...@@ -31,7 +30,7 @@ class MixedViewport extends RenderObjectWidget {
final Axis direction; final Axis direction;
final IndexedBuilder builder; final IndexedBuilder builder;
final Object token; // change this if the list changed (i.e. there are added, removed, or resorted items) final Object token; // change this if the list changed (i.e. there are added, removed, or resorted items)
final ExtentsUpdateCallback onExtentsUpdate; final ValueChanged<double> onExtentChanged;
final InvalidatorAvailableCallback onInvalidatorAvailable; // call the callback this gives to invalidate sizes final InvalidatorAvailableCallback onInvalidatorAvailable; // call the callback this gives to invalidate sizes
_MixedViewportElement createElement() => new _MixedViewportElement(this); _MixedViewportElement createElement() => new _MixedViewportElement(this);
...@@ -108,8 +107,8 @@ class _MixedViewportElement extends RenderObjectElement<MixedViewport> { ...@@ -108,8 +107,8 @@ class _MixedViewportElement extends RenderObjectElement<MixedViewport> {
/// The constraints for which the current offsets are valid. /// The constraints for which the current offsets are valid.
BoxConstraints _lastLayoutConstraints; BoxConstraints _lastLayoutConstraints;
/// The last value that was sent to onExtentsUpdate. /// The last value that was sent to onExtentChanged.
double _lastReportedExtents; double _lastReportedExtent;
RenderBlockViewport get renderObject => super.renderObject; RenderBlockViewport get renderObject => super.renderObject;
...@@ -227,11 +226,11 @@ class _MixedViewportElement extends RenderObjectElement<MixedViewport> { ...@@ -227,11 +226,11 @@ class _MixedViewportElement extends RenderObjectElement<MixedViewport> {
BuildableElement.lockState(() { BuildableElement.lockState(() {
_doLayout(constraints); _doLayout(constraints);
}, building: true); }, building: true);
if (widget.onExtentsUpdate != null) { if (widget.onExtentChanged != null) {
final double newExtents = _didReachLastChild ? _childOffsets.last : null; final double newExtent = _didReachLastChild ? _childOffsets.last : null;
if (newExtents != _lastReportedExtents) { if (newExtent != _lastReportedExtent) {
_lastReportedExtents = newExtents; _lastReportedExtent = newExtent;
widget.onExtentsUpdate(_lastReportedExtents); widget.onExtentChanged(_lastReportedExtent);
} }
} }
} }
......
...@@ -35,6 +35,15 @@ abstract class ScrollBehavior<T, U> { ...@@ -35,6 +35,15 @@ abstract class ScrollBehavior<T, U> {
/// Whether this scroll behavior currently permits scrolling /// Whether this scroll behavior currently permits scrolling
bool get isScrollable => true; bool get isScrollable => true;
String toString() {
List<String> description = <String>[];
debugFillDescription(description);
return '$runtimeType(${description.join("; ")})';
}
void debugFillDescription(List<String> description) {
description.add(isScrollable ? 'scrollable' : 'not scrollable');
}
} }
/// A scroll behavior for a scrollable widget with linear extent (i.e. /// A scroll behavior for a scrollable widget with linear extent (i.e.
...@@ -74,6 +83,13 @@ abstract class ExtentScrollBehavior extends ScrollBehavior<double, double> { ...@@ -74,6 +83,13 @@ abstract class ExtentScrollBehavior extends ScrollBehavior<double, double> {
/// The maximum value the scroll offset can obtain. /// The maximum value the scroll offset can obtain.
double get maxScrollOffset; double get maxScrollOffset;
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('content: ${contentExtent.toStringAsFixed(1)}');
description.add('container: ${contentExtent.toStringAsFixed(1)}');
description.add('range: ${minScrollOffset?.toStringAsFixed(1)} .. ${maxScrollOffset?.toStringAsFixed(1)}');
}
} }
/// A scroll behavior that prevents the user from exceeding scroll bounds. /// A scroll behavior that prevents the user from exceeding scroll bounds.
......
...@@ -237,32 +237,42 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -237,32 +237,42 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
return _scrollBehavior; return _scrollBehavior;
} }
GestureDragStartCallback _getDragStartHandler(Axis direction) { Map<Type, GestureRecognizerFactory> buildGestureDetectors() {
if (config.scrollDirection != direction || !scrollBehavior.isScrollable) if (scrollBehavior.isScrollable) {
return null; switch (config.scrollDirection) {
return _handleDragStart; case Axis.vertical:
return <Type, GestureRecognizerFactory>{
VerticalDragGestureRecognizer: (VerticalDragGestureRecognizer recognizer) {
return (recognizer ??= new VerticalDragGestureRecognizer())
..onStart = _handleDragStart
..onUpdate = _handleDragUpdate
..onEnd = _handleDragEnd;
}
};
case Axis.horizontal:
return <Type, GestureRecognizerFactory>{
HorizontalDragGestureRecognizer: (HorizontalDragGestureRecognizer recognizer) {
return (recognizer ??= new HorizontalDragGestureRecognizer())
..onStart = _handleDragStart
..onUpdate = _handleDragUpdate
..onEnd = _handleDragEnd;
}
};
}
}
return const <Type, GestureRecognizerFactory>{};
} }
GestureDragUpdateCallback _getDragUpdateHandler(Axis direction) { final GlobalKey _gestureDetectorKey = new GlobalKey();
if (config.scrollDirection != direction || !scrollBehavior.isScrollable)
return null;
return _handleDragUpdate;
}
GestureDragEndCallback _getDragEndHandler(Axis direction) { void updateGestureDetector() {
if (config.scrollDirection != direction || !scrollBehavior.isScrollable) _gestureDetectorKey.currentState.replaceGestureRecognizers(buildGestureDetectors());
return null;
return _handleDragEnd;
} }
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new GestureDetector( return new RawGestureDetector(
onVerticalDragStart: _getDragStartHandler(Axis.vertical), key: _gestureDetectorKey,
onVerticalDragUpdate: _getDragUpdateHandler(Axis.vertical), gestures: buildGestureDetectors(),
onVerticalDragEnd: _getDragEndHandler(Axis.vertical),
onHorizontalDragStart: _getDragStartHandler(Axis.horizontal),
onHorizontalDragUpdate: _getDragUpdateHandler(Axis.horizontal),
onHorizontalDragEnd: _getDragEndHandler(Axis.horizontal),
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
child: new Listener( child: new Listener(
child: buildContent(context), child: buildContent(context),
...@@ -321,7 +331,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -321,7 +331,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
if (endScrollOffset.isNaN) if (endScrollOffset.isNaN)
return null; return null;
final double snappedScrollOffset = snapScrollOffset(endScrollOffset); final double snappedScrollOffset = snapScrollOffset(endScrollOffset); // invokes the config.snapOffsetCallback callback
if (!_scrollOffsetIsInBounds(snappedScrollOffset)) if (!_scrollOffsetIsInBounds(snappedScrollOffset))
return null; return null;
...@@ -443,7 +453,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -443,7 +453,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
} }
void _handleDragStart(_) { void _handleDragStart(_) {
scheduleMicrotask(dispatchOnScrollStart); dispatchOnScrollStart();
} }
void _handleDragUpdate(double delta) { void _handleDragUpdate(double delta) {
...@@ -503,18 +513,19 @@ class _ScrollableViewportState extends ScrollableState<ScrollableViewport> { ...@@ -503,18 +513,19 @@ class _ScrollableViewportState extends ScrollableState<ScrollableViewport> {
double _viewportSize = 0.0; double _viewportSize = 0.0;
double _childSize = 0.0; double _childSize = 0.0;
void _handleViewportSizeChanged(Size newSize) {
_viewportSize = config.scrollDirection == Axis.vertical ? newSize.height : newSize.width; Offset _handlePaintOffsetUpdateNeeded(ViewportDimensions dimensions) {
setState(() { // We make various state changes here but don't have to do so in a
_updateScrollBehavior(); // setState() callback because we are called during layout and all
}); // we're updating is the new offset, which we are providing to the
} // render object via our return value.
void _handleChildSizeChanged(Size newSize) { _viewportSize = config.scrollDirection == Axis.vertical ? dimensions.containerSize.height : dimensions.containerSize.width;
_childSize = config.scrollDirection == Axis.vertical ? newSize.height : newSize.width; _childSize = config.scrollDirection == Axis.vertical ? dimensions.contentSize.height : dimensions.contentSize.width;
setState(() { _updateScrollBehavior();
_updateScrollBehavior(); updateGestureDetector();
}); return scrollOffsetToPixelDelta(scrollOffset);
} }
void _updateScrollBehavior() { void _updateScrollBehavior() {
// if you don't call this from build(), you must call it from setState(). // if you don't call this from build(), you must call it from setState().
scrollTo(scrollBehavior.updateExtents( scrollTo(scrollBehavior.updateExtents(
...@@ -525,17 +536,12 @@ class _ScrollableViewportState extends ScrollableState<ScrollableViewport> { ...@@ -525,17 +536,12 @@ class _ScrollableViewportState extends ScrollableState<ScrollableViewport> {
} }
Widget buildContent(BuildContext context) { Widget buildContent(BuildContext context) {
return new SizeObserver( return new Viewport(
onSizeChanged: _handleViewportSizeChanged, paintOffset: scrollOffsetToPixelDelta(scrollOffset),
child: new Viewport( scrollDirection: config.scrollDirection,
paintOffset: scrollOffsetToPixelDelta(scrollOffset), scrollAnchor: config.scrollAnchor,
scrollDirection: config.scrollDirection, onPaintOffsetUpdateNeeded: _handlePaintOffsetUpdateNeeded,
scrollAnchor: config.scrollAnchor, child: config.child
child: new SizeObserver(
onSizeChanged: _handleChildSizeChanged,
child: config.child
)
)
); );
} }
} }
...@@ -690,11 +696,11 @@ class ScrollableMixedWidgetListState extends ScrollableState<ScrollableMixedWidg ...@@ -690,11 +696,11 @@ class ScrollableMixedWidgetListState extends ScrollableState<ScrollableMixedWidg
} }
} }
void _handleExtentsUpdate(double newExtents) { void _handleExtentChanged(double newExtent) {
double newScrollOffset; double newScrollOffset;
setState(() { setState(() {
newScrollOffset = scrollBehavior.updateExtents( newScrollOffset = scrollBehavior.updateExtents(
contentExtent: newExtents ?? double.INFINITY, contentExtent: newExtent ?? double.INFINITY,
scrollOffset: scrollOffset scrollOffset: scrollOffset
); );
}); });
...@@ -712,7 +718,7 @@ class ScrollableMixedWidgetListState extends ScrollableState<ScrollableMixedWidg ...@@ -712,7 +718,7 @@ class ScrollableMixedWidgetListState extends ScrollableState<ScrollableMixedWidg
builder: config.builder, builder: config.builder,
token: config.token, token: config.token,
onInvalidatorAvailable: config.onInvalidatorAvailable, onInvalidatorAvailable: config.onInvalidatorAvailable,
onExtentsUpdate: _handleExtentsUpdate onExtentChanged: _handleExtentChanged
) )
); );
} }
......
...@@ -50,16 +50,18 @@ void main() { ...@@ -50,16 +50,18 @@ void main() {
] ]
) )
); );
tester.pump(); // for SizeObservers
Point middleOfContainer = tester.getCenter(tester.findText('Hello')); Point middleOfContainer = tester.getCenter(tester.findText('Hello'));
expect(middleOfContainer.x, equals(400.0));
expect(middleOfContainer.y, equals(1000.0));
Point target = tester.getCenter(tester.findElementByKey(blockKey)); Point target = tester.getCenter(tester.findElementByKey(blockKey));
TestGesture gesture = tester.startGesture(target); TestGesture gesture = tester.startGesture(target);
gesture.moveBy(const Offset(0.0, -10.0)); gesture.moveBy(const Offset(0.0, -10.0));
tester.pump(const Duration(milliseconds: 1)); tester.pump(); // redo layout
expect(tester.getCenter(tester.findText('Hello')) == middleOfContainer, isFalse); expect(tester.getCenter(tester.findText('Hello')), isNot(equals(middleOfContainer)));
gesture.up(); gesture.up();
}); });
......
...@@ -6,6 +6,7 @@ import 'dart:ui' as ui show window; ...@@ -6,6 +6,7 @@ import 'dart:ui' as ui show window;
import 'package:quiver/testing/async.dart'; import 'package:quiver/testing/async.dart';
import 'package:quiver/time.dart'; import 'package:quiver/time.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -57,6 +58,11 @@ class WidgetTester extends Instrumentation { ...@@ -57,6 +58,11 @@ class WidgetTester extends Instrumentation {
); );
async.flushMicrotasks(); async.flushMicrotasks();
} }
void dispatchEvent(PointerEvent event, HitTestResult result) {
super.dispatchEvent(event, result);
async.flushMicrotasks();
}
} }
void testWidgets(callback(WidgetTester tester)) { void testWidgets(callback(WidgetTester tester)) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment