diff --git a/sky/packages/sky/lib/src/rendering/box.dart b/sky/packages/sky/lib/src/rendering/box.dart index f13e16e62..91ba89c54 100644 --- a/sky/packages/sky/lib/src/rendering/box.dart +++ b/sky/packages/sky/lib/src/rendering/box.dart @@ -280,6 +280,53 @@ abstract class RenderBox extends RenderObject { return constraints.constrainHeight(0.0); } + /// The size of this render box computed during layout + /// + /// This value is stale whenever this object is marked as needing layout. + /// During [performLayout], do not read the size of a child unless you pass + /// true for parentUsesSize when calling the child's [layout] function. + /// + /// The size of a box should be set only during the box's [performLayout] or + /// [performResize] functions. If you wish to change the size of a box outside + /// of those functins, call [markNeedsLayout] instead to schedule a layout of + /// the box. + Size get size { + assert(hasSize); + assert(() { + if (_size is _DebugSize) { + final _DebugSize _size = this._size; // TODO(ianh): Remove this once the analyzer is cleverer + assert(_size._owner == this); + if (RenderObject.debugActiveLayout != null) { + // we are always allowed to access our own size (for print debugging and asserts if nothing else) + // other than us, the only object that's allowed to read our size is our parent, if they're said they will + // if you hit this assert trying to access a child's size, pass parentUsesSize: true in layout() + assert(debugDoingThisResize || debugDoingThisLayout || + (RenderObject.debugActiveLayout == parent && _size._canBeUsedByParent)); + } + assert(_size == this._size); // TODO(ianh): Remove this once the analyzer is cleverer + } + return true; + }); + return _size; + } + bool get hasSize => _size != null; + Size _size; + void set size(Size value) { + assert((sizedByParent && debugDoingThisResize) || + (!sizedByParent && debugDoingThisLayout)); + assert(() { + if (value is _DebugSize) + return value._canBeUsedByParent && value._owner.parent == this; + return true; + }); + _size = value; + assert(() { + _size = new _DebugSize(_size, this, debugCanParentUseSize); + return true; + }); + assert(debugDoesMeetConstraints()); + } + Map _cachedBaselines; bool _ancestorUsesBaseline = false; static bool _debugDoingBaseline = false; @@ -427,56 +474,6 @@ abstract class RenderBox extends RenderObject { /// visually "on top" (i.e., paints later). void hitTestChildren(HitTestResult result, { Point position }) { } - // TODO(ianh): move size up to before constraints - // TODO(ianh): In non-debug builds, this should all just be: - // Size size = Size.zero; - // In debug builds, however: - Size _size = Size.zero; - - /// The size of this render box computed during layout - /// - /// This value is stale whenever this object is marked as needing layout. - /// During [performLayout], do not read the size of a child unless you pass - /// true for parentUsesSize when calling the child's [layout] function. - /// - /// The size of a box should be set only during the box's [performLayout] or - /// [performResize] functions. If you wish to change the size of a box outside - /// of those functins, call [markNeedsLayout] instead to schedule a layout of - /// the box. - Size get size { - assert(() { - if (_size is _DebugSize) { - final _DebugSize _size = this._size; // TODO(ianh): Remove this once the analyzer is cleverer - assert(_size._owner == this); - if (RenderObject.debugActiveLayout != null) { - // we are always allowed to access our own size (for print debugging and asserts if nothing else) - // other than us, the only object that's allowed to read our size is our parent, if they're said they will - // if you hit this assert trying to access a child's size, pass parentUsesSize: true in layout() - assert(debugDoingThisResize || debugDoingThisLayout || - (RenderObject.debugActiveLayout == parent && _size._canBeUsedByParent)); - } - assert(_size == this._size); // TODO(ianh): Remove this once the analyzer is cleverer - } - return true; - }); - return _size; - } - void set size(Size value) { - assert((sizedByParent && debugDoingThisResize) || - (!sizedByParent && debugDoingThisLayout)); - assert(() { - if (value is _DebugSize) - return value._canBeUsedByParent && value._owner.parent == this; - return true; - }); - _size = value; - assert(() { - _size = new _DebugSize(_size, this, debugCanParentUseSize); - return true; - }); - assert(debugDoesMeetConstraints()); - } - /// Multiply the transform from the parent's coordinate system to this box's /// coordinate system into the given transform /// diff --git a/sky/packages/sky/lib/src/rendering/proxy_box.dart b/sky/packages/sky/lib/src/rendering/proxy_box.dart index 8f3d50fdc..7bd0b9a42 100644 --- a/sky/packages/sky/lib/src/rendering/proxy_box.dart +++ b/sky/packages/sky/lib/src/rendering/proxy_box.dart @@ -963,7 +963,7 @@ class RenderSizeObserver extends RenderProxyBox { SizeChangedCallback callback; void performLayout() { - Size oldSize = size; + Size oldSize = hasSize ? size : null; super.performLayout(); if (oldSize != size) callback(size); diff --git a/sky/unit/test/widget/size_observer_test.dart b/sky/unit/test/widget/size_observer_test.dart new file mode 100644 index 000000000..6b9b1cadb --- /dev/null +++ b/sky/unit/test/widget/size_observer_test.dart @@ -0,0 +1,48 @@ + +import 'package:sky/rendering.dart'; +import 'package:sky/widgets.dart'; +import 'package:test/test.dart'; + +import 'widget_tester.dart'; + +void main() { + test('SizeObserver notices zero size', () { + testWidgets((WidgetTester tester) { + List results = []; + tester.pumpWidget(new Center( + child: new SizeObserver( + callback: (size) { results.add(size); }, + child: new Container(width:0.0, height:0.0) + ) + )); + expect(results, equals([Size.zero])); + tester.pump(); + expect(results, equals([Size.zero])); + tester.pumpWidget(new Center( + child: new SizeObserver( + callback: (size) { results.add(size); }, + child: new Container(width:100.0, height:0.0) + ) + )); + expect(results, equals([Size.zero, const Size(100.0, 0.0)])); + tester.pump(); + expect(results, equals([Size.zero, const Size(100.0, 0.0)])); + tester.pumpWidget(new Center( + child: new SizeObserver( + callback: (size) { results.add(size); }, + child: new Container(width:0.0, height:0.0) + ) + )); + expect(results, equals([Size.zero, const Size(100.0, 0.0), Size.zero])); + tester.pump(); + expect(results, equals([Size.zero, const Size(100.0, 0.0), Size.zero])); + tester.pumpWidget(new Center( + child: new SizeObserver( + callback: (size) { results.add(size); }, + child: new Container(width:0.0, height:0.0) + ) + )); + expect(results, equals([Size.zero, const Size(100.0, 0.0), Size.zero])); + }); + }); +}