diff --git a/packages/flutter/lib/src/cupertino/dialog.dart b/packages/flutter/lib/src/cupertino/dialog.dart index 6d10cc851a..01bd7866f3 100644 --- a/packages/flutter/lib/src/cupertino/dialog.dart +++ b/packages/flutter/lib/src/cupertino/dialog.dart @@ -1566,7 +1566,9 @@ class _ActionButtonParentDataWidget parentData.isPressed = isPressed; // Force a repaint. - renderObject.parent?.markNeedsPaint(); + final AbstractNode? targetParent = renderObject.parent; + if (targetParent is RenderObject) + targetParent.markNeedsPaint(); } } diff --git a/packages/flutter/lib/src/material/data_table.dart b/packages/flutter/lib/src/material/data_table.dart index 069e17ad6a..11c8ea330d 100644 --- a/packages/flutter/lib/src/material/data_table.dart +++ b/packages/flutter/lib/src/material/data_table.dart @@ -4,6 +4,7 @@ import 'dart:math' as math; +import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; @@ -1085,9 +1086,9 @@ class TableRowInkWell extends InkResponse { RectCallback getRectCallback(RenderBox referenceBox) { return () { RenderObject cell = referenceBox; - RenderObject? table = cell.parent; + AbstractNode? table = cell.parent; final Matrix4 transform = Matrix4.identity(); - while (table != null && table is! RenderTable) { + while (table is RenderObject && table is! RenderTable) { table.applyPaintTransform(cell, transform); assert(table == cell.parent); cell = table; diff --git a/packages/flutter/lib/src/material/material.dart b/packages/flutter/lib/src/material/material.dart index 50b253d2f5..1542228e30 100644 --- a/packages/flutter/lib/src/material/material.dart +++ b/packages/flutter/lib/src/material/material.dart @@ -683,7 +683,7 @@ abstract class InkFeature { RenderObject node = referenceBox; while (node != _controller) { final RenderObject childNode = node; - node = node.parent!; + node = node.parent! as RenderObject; if (!node.paintsChild(childNode)) { // Some node between the reference box and this would skip painting on // the reference box, so bail out early and avoid unnecessary painting. diff --git a/packages/flutter/lib/src/rendering/box.dart b/packages/flutter/lib/src/rendering/box.dart index 453f2cf07a..563414b5c9 100644 --- a/packages/flutter/lib/src/rendering/box.dart +++ b/packages/flutter/lib/src/rendering/box.dart @@ -2145,7 +2145,7 @@ abstract class RenderBox extends RenderObject { assert(!_debugDoingBaseline, 'Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.'); assert(!debugNeedsLayout); assert(() { - final RenderObject? parent = this.parent; + final RenderObject? parent = this.parent as RenderObject?; if (owner!.debugDoingLayout) return (RenderObject.debugActiveLayout == parent) && parent!.debugDoingThisLayout; if (owner!.debugDoingPaint) @@ -2367,7 +2367,7 @@ abstract class RenderBox extends RenderObject { @override void markNeedsLayout() { - if (_clearCachedData() && parent != null) { + if (_clearCachedData() && parent is RenderObject) { markParentNeedsLayout(); return; } diff --git a/packages/flutter/lib/src/rendering/list_wheel_viewport.dart b/packages/flutter/lib/src/rendering/list_wheel_viewport.dart index 556d54ca3f..b9a81899a0 100644 --- a/packages/flutter/lib/src/rendering/list_wheel_viewport.dart +++ b/packages/flutter/lib/src/rendering/list_wheel_viewport.dart @@ -1039,7 +1039,7 @@ class RenderListWheelViewport // `child` will be the last RenderObject before the viewport when walking up from `target`. RenderObject child = target; while (child.parent != this) - child = child.parent!; + child = child.parent! as RenderObject; final ListWheelParentData parentData = child.parentData! as ListWheelParentData; final double targetOffset = parentData.offset.dy; // the so-called "centerPosition" diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart index 6d8584e04d..ffdda2de0c 100644 --- a/packages/flutter/lib/src/rendering/object.dart +++ b/packages/flutter/lib/src/rendering/object.dart @@ -909,10 +909,12 @@ class PipelineOwner { onNeedVisualUpdate?.call(); } - /// The unique [RenderObject] managed by this pipeline that has no parent. - RenderObject? get rootNode => _rootNode; - RenderObject? _rootNode; - set rootNode(RenderObject? value) { + /// The unique object managed by this pipeline that has no parent. + /// + /// This object does not have to be a [RenderObject]. + AbstractNode? get rootNode => _rootNode; + AbstractNode? _rootNode; + set rootNode(AbstractNode? value) { if (_rootNode == value) return; _rootNode?.detach(); @@ -1320,174 +1322,13 @@ class PipelineOwner { /// [RenderObject.markNeedsLayout] so that if a parent has queried the intrinsic /// or baseline information, it gets marked dirty whenever the child's geometry /// changes. -abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarget { +abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget { /// Initializes internal fields for subclasses. RenderObject() { _needsCompositing = isRepaintBoundary || alwaysNeedsCompositing; _wasRepaintBoundary = isRepaintBoundary; } - /// The depth of this node in the tree. - /// - /// The depth of nodes in a tree monotonically increases as you traverse down - /// the tree. - int get depth => _depth; - int _depth = 0; - - /// Adjust the [depth] of the given [child] to be greater than this node's own - /// [depth]. - /// - /// Only call this method from overrides of [redepthChildren]. - @protected - void redepthChild(RenderObject child) { - assert(child.owner == owner); - if (child._depth <= _depth) { - child._depth = _depth + 1; - child.redepthChildren(); - } - } - - /// Adjust the [depth] of this node's children, if any. - /// - /// Override this method in subclasses with child nodes to call [redepthChild] - /// for each child. Do not call this method directly. - void redepthChildren() { } - - /// The owner for this node (null if unattached). - /// - /// The entire subtree that this node belongs to will have the same owner. - PipelineOwner? get owner => _owner; - PipelineOwner? _owner; - - /// Whether this node is in a tree whose root is attached to something. - /// - /// This becomes true during the call to [attach]. - /// - /// This becomes false during the call to [detach]. - bool get attached => _owner != null; - - /// Mark this node as attached to the given owner. - /// - /// Typically called only from the [parent]'s [attach] method, and by the - /// [owner] to mark the root of a tree as attached. - /// - /// Subclasses with children should override this method to first call their - /// inherited [attach] method, and then [attach] all their children to the - /// same [owner]. - /// - /// Implementations of this method should start with a call to the inherited - /// method, as in `super.attach(owner)`. - @mustCallSuper - void attach(PipelineOwner owner) { - assert(!_debugDisposed); - assert(owner != null); - assert(_owner == null); - _owner = owner; - - // If the node was dirtied in some way while unattached, make sure to add - // it to the appropriate dirty list now that an owner is available - if (_needsLayout && _relayoutBoundary != null) { - // Don't enter this block if we've never laid out at all; - // scheduleInitialLayout() will handle it - _needsLayout = false; - markNeedsLayout(); - } - if (_needsCompositingBitsUpdate) { - _needsCompositingBitsUpdate = false; - markNeedsCompositingBitsUpdate(); - } - if (_needsPaint && _layerHandle.layer != null) { - // Don't enter this block if we've never painted at all; - // scheduleInitialPaint() will handle it - _needsPaint = false; - markNeedsPaint(); - } - if (_needsSemanticsUpdate && _semanticsConfiguration.isSemanticBoundary) { - // Don't enter this block if we've never updated semantics at all; - // scheduleInitialSemantics() will handle it - _needsSemanticsUpdate = false; - markNeedsSemanticsUpdate(); - } - } - - /// Mark this node as detached. - /// - /// Typically called only from the [parent]'s [detach], and by the [owner] to - /// mark the root of a tree as detached. - /// - /// Subclasses with children should override this method to first call their - /// inherited [detach] method, and then [detach] all their children. - /// - /// Implementations of this method should end with a call to the inherited - /// method, as in `super.detach()`. - @mustCallSuper - void detach() { - assert(_owner != null); - _owner = null; - assert(parent == null || attached == parent!.attached); - } - - /// The parent of this node in the tree. - RenderObject? get parent => _parent; - RenderObject? _parent; - - /// Called by subclasses when they decide a render object is a child. - /// - /// Only for use by subclasses when changing their child lists. Calling this - /// in other cases will lead to an inconsistent tree and probably cause crashes. - @protected - @mustCallSuper - void adoptChild(RenderObject child) { - assert(_debugCanPerformMutations); - assert(child != null); - - setupParentData(child); - markNeedsLayout(); - markNeedsCompositingBitsUpdate(); - markNeedsSemanticsUpdate(); - - assert(child != null); - assert(child._parent == null); - assert(() { - RenderObject node = this; - while (node.parent != null) - node = node.parent!; - assert(node != child); // indicates we are about to create a cycle - return true; - }()); - child._parent = this; - if (attached) - child.attach(_owner!); - redepthChild(child); - } - - /// Called by subclasses when they decide a render object is no longer a child. - /// - /// Only for use by subclasses when changing their child lists. Calling this - /// in other cases will lead to an inconsistent tree and probably cause crashes. - @protected - @mustCallSuper - void dropChild(RenderObject child) { - assert(_debugCanPerformMutations); - assert(child != null); - assert(child.parentData != null); - - child._cleanRelayoutBoundary(); - child.parentData!.detach(); - child.parentData = null; - - assert(child != null); - assert(child._parent == this); - assert(child.attached == attached); - child._parent = null; - if (attached) - child.detach(); - - markNeedsLayout(); - markNeedsCompositingBitsUpdate(); - markNeedsSemanticsUpdate(); - } - /// Cause the entire subtree rooted at the given [RenderObject] to be marked /// dirty for layout, paint, etc, so that the effects of a hot reload can be /// seen, or so that the effect of changing a global debug flag (such as @@ -1588,6 +1429,39 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge child.parentData = ParentData(); } + /// Called by subclasses when they decide a render object is a child. + /// + /// Only for use by subclasses when changing their child lists. Calling this + /// in other cases will lead to an inconsistent tree and probably cause crashes. + @override + void adoptChild(RenderObject child) { + assert(_debugCanPerformMutations); + assert(child != null); + setupParentData(child); + markNeedsLayout(); + markNeedsCompositingBitsUpdate(); + markNeedsSemanticsUpdate(); + super.adoptChild(child); + } + + /// Called by subclasses when they decide a render object is no longer a child. + /// + /// Only for use by subclasses when changing their child lists. Calling this + /// in other cases will lead to an inconsistent tree and probably cause crashes. + @override + void dropChild(RenderObject child) { + assert(_debugCanPerformMutations); + assert(child != null); + assert(child.parentData != null); + child._cleanRelayoutBoundary(); + child.parentData!.detach(); + child.parentData = null; + super.dropChild(child); + markNeedsLayout(); + markNeedsCompositingBitsUpdate(); + markNeedsSemanticsUpdate(); + } + /// Calls visitor for each immediate child of this render object. /// /// Override in subclasses with children and call the visitor for each child. @@ -1687,17 +1561,50 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge result = false; break; } - if (node.parent == null) { + if (node.parent is! RenderObject) { result = true; break; } - node = node.parent!; + node = node.parent! as RenderObject; } return true; }()); return result; } + @override + PipelineOwner? get owner => super.owner as PipelineOwner?; + + @override + void attach(PipelineOwner owner) { + assert(!_debugDisposed); + super.attach(owner); + // If the node was dirtied in some way while unattached, make sure to add + // it to the appropriate dirty list now that an owner is available + if (_needsLayout && _relayoutBoundary != null) { + // Don't enter this block if we've never laid out at all; + // scheduleInitialLayout() will handle it + _needsLayout = false; + markNeedsLayout(); + } + if (_needsCompositingBitsUpdate) { + _needsCompositingBitsUpdate = false; + markNeedsCompositingBitsUpdate(); + } + if (_needsPaint && _layerHandle.layer != null) { + // Don't enter this block if we've never painted at all; + // scheduleInitialPaint() will handle it + _needsPaint = false; + markNeedsPaint(); + } + if (_needsSemanticsUpdate && _semanticsConfiguration.isSemanticBoundary) { + // Don't enter this block if we've never updated semantics at all; + // scheduleInitialSemantics() will handle it + _needsSemanticsUpdate = false; + markNeedsSemanticsUpdate(); + } + } + /// Whether this render object's layout information is dirty. /// /// This is only set in debug mode. In general, render objects should not need @@ -1759,7 +1666,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge while (node != _relayoutBoundary) { assert(node._relayoutBoundary == _relayoutBoundary); assert(node.parent != null); - node = node.parent!; + node = node.parent! as RenderObject; if ((!node._needsLayout) && (!node._debugDoingThisLayout)) return false; } @@ -1850,7 +1757,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge void markParentNeedsLayout() { _needsLayout = true; assert(this.parent != null); - final RenderObject parent = this.parent!; + final RenderObject parent = this.parent! as RenderObject; if (!_doingThisLayoutWithCallback) { parent.markNeedsLayout(); } else { @@ -1882,7 +1789,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge if (_relayoutBoundary == this) { return; } - final RenderObject? parentRelayoutBoundary = parent?._relayoutBoundary; + final RenderObject? parentRelayoutBoundary = (parent as RenderObject?)?._relayoutBoundary; assert(parentRelayoutBoundary != null); if (parentRelayoutBoundary != _relayoutBoundary) { _relayoutBoundary = parentRelayoutBoundary; @@ -1908,7 +1815,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge void scheduleInitialLayout() { assert(!_debugDisposed); assert(attached); - assert(parent == null); + assert(parent is! RenderObject); assert(!owner!._debugDoingLayout); assert(_relayoutBoundary == null); _relayoutBoundary = this; @@ -2022,8 +1929,8 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge )); assert(!_debugDoingThisResize); assert(!_debugDoingThisLayout); - final bool isRelayoutBoundary = !parentUsesSize || sizedByParent || constraints.isTight || parent == null; - final RenderObject relayoutBoundary = isRelayoutBoundary ? this : parent!._relayoutBoundary!; + final bool isRelayoutBoundary = !parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject; + final RenderObject relayoutBoundary = isRelayoutBoundary ? this : (parent! as RenderObject)._relayoutBoundary!; assert(() { _debugCanParentUseSize = parentUsesSize; return true; @@ -2393,8 +2300,8 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge if (_needsCompositingBitsUpdate) return; _needsCompositingBitsUpdate = true; - if (parent != null) { - final RenderObject parent = this.parent!; + if (parent is RenderObject) { + final RenderObject parent = this.parent! as RenderObject; if (parent._needsCompositingBitsUpdate) return; @@ -2535,8 +2442,8 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge owner!._nodesNeedingPaint.add(this); owner!.requestVisualUpdate(); } - } else if (parent != null) { - final RenderObject parent = this.parent!; + } else if (parent is RenderObject) { + final RenderObject parent = this.parent! as RenderObject; parent.markNeedsPaint(); assert(parent == this.parent); } else { @@ -2604,8 +2511,8 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge assert(_needsPaint || _needsCompositedLayerUpdate); assert(_layerHandle.layer != null); assert(!_layerHandle.layer!.attached); - RenderObject? node = parent; - while (node != null) { + AbstractNode? node = parent; + while (node is RenderObject) { if (node.isRepaintBoundary) { if (node._layerHandle.layer == null) break; // looks like the subtree here has never been painted. let it handle itself. @@ -2626,7 +2533,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge void scheduleInitialPaint(ContainerLayer rootLayer) { assert(rootLayer.attached); assert(attached); - assert(parent == null); + assert(parent is! RenderObject); assert(!owner!._debugDoingPaint); assert(isRepaintBoundary); assert(_layerHandle.layer == null); @@ -2644,7 +2551,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge assert(!_debugDisposed); assert(rootLayer.attached); assert(attached); - assert(parent == null); + assert(parent is! RenderObject); assert(!owner!._debugDoingPaint); assert(isRepaintBoundary); assert(_layerHandle.layer != null); // use scheduleInitialPaint the first time @@ -2695,8 +2602,8 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge } assert(() { if (_needsCompositingBitsUpdate) { - if (parent != null) { - final RenderObject parent = this.parent!; + if (parent is RenderObject) { + final RenderObject parent = this.parent! as RenderObject; bool visitedByParent = false; parent.visitChildren((RenderObject child) { if (child == this) { @@ -2854,12 +2761,12 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge final bool ancestorSpecified = ancestor != null; assert(attached); if (ancestor == null) { - final RenderObject? rootNode = owner!.rootNode; - if (rootNode != null) + final AbstractNode? rootNode = owner!.rootNode; + if (rootNode is RenderObject) ancestor = rootNode; } final List renderers = []; - for (RenderObject renderer = this; renderer != ancestor; renderer = renderer.parent!) { + for (RenderObject renderer = this; renderer != ancestor; renderer = renderer.parent! as RenderObject) { renderers.add(renderer); assert(renderer.parent != null); // Failed to find ancestor in parent chain. } @@ -2927,7 +2834,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge void scheduleInitialSemantics() { assert(!_debugDisposed); assert(attached); - assert(parent == null); + assert(parent is! RenderObject); assert(!owner!._debugDoingSemantics); assert(_semantics == null); assert(_needsSemanticsUpdate); @@ -2989,7 +2896,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge if (_semantics != null && !_semantics!.isMergedIntoParent) { _semantics!.sendEvent(semanticsEvent); } else if (parent != null) { - final RenderObject renderParent = parent!; + final RenderObject renderParent = parent! as RenderObject; renderParent.sendSemanticsEvent(semanticsEvent); } } @@ -3065,12 +2972,12 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge bool isEffectiveSemanticsBoundary = _semanticsConfiguration.isSemanticBoundary && wasSemanticsBoundary; RenderObject node = this; - while (!isEffectiveSemanticsBoundary && node.parent != null) { + while (!isEffectiveSemanticsBoundary && node.parent is RenderObject) { if (node != this && node._needsSemanticsUpdate) break; node._needsSemanticsUpdate = true; - node = node.parent!; + node = node.parent! as RenderObject; isEffectiveSemanticsBoundary = node._semanticsConfiguration.isSemanticBoundary; if (isEffectiveSemanticsBoundary && node._semantics == null) { // We have reached a semantics boundary that doesn't own a semantics node. @@ -3092,7 +2999,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge if (!node._needsSemanticsUpdate) { node._needsSemanticsUpdate = true; if (owner != null) { - assert(node._semanticsConfiguration.isSemanticBoundary || node.parent == null); + assert(node._semanticsConfiguration.isSemanticBoundary || node.parent is! RenderObject); owner!._nodesNeedingSemantics.add(node); owner!.requestVisualUpdate(); } @@ -3101,7 +3008,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge /// Updates the semantic information of the render object. void _updateSemantics() { - assert(_semanticsConfiguration.isSemanticBoundary || parent == null); + assert(_semanticsConfiguration.isSemanticBoundary || parent is! RenderObject); if (_needsLayout) { // There's not enough information in this subtree to compute semantics. // The subtree is probably being kept alive by a viewport but not laid out. @@ -3155,7 +3062,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge fragments.add(fragment); fragment.addAncestor(this); fragment.addTags(config.tagsForChildren); - if (config.explicitChildNodes || parent == null) { + if (config.explicitChildNodes || parent is! RenderObject) { fragment.markAsExplicit(); continue; } @@ -3180,7 +3087,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge _needsSemanticsUpdate = false; _SemanticsFragment result; - if (parent == null) { + if (parent is! RenderObject) { assert(!config.hasBeenAnnotated); assert(!mergeIntoParent); result = _RootSemanticsFragment( @@ -3283,9 +3190,9 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge } if (_relayoutBoundary != null && _relayoutBoundary != this) { int count = 1; - RenderObject? target = parent ; + RenderObject? target = parent as RenderObject?; while (target != null && target != _relayoutBoundary) { - target = target.parent; + target = target.parent as RenderObject?; count += 1; } header += ' relayoutBoundary=up$count'; @@ -3404,8 +3311,8 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge Duration duration = Duration.zero, Curve curve = Curves.ease, }) { - if (parent != null) { - final RenderObject renderParent = parent!; + if (parent is RenderObject) { + final RenderObject renderParent = parent! as RenderObject; renderParent.showOnScreen( descendant: descendant ?? this, rect: rect, @@ -4329,12 +4236,12 @@ class _SemanticsGeometry { assert(transform != null); assert(clipRectTransform != null); assert(clipRectTransform.isIdentity()); - RenderObject intermediateParent = child.parent!; + RenderObject intermediateParent = child.parent! as RenderObject; assert(intermediateParent != null); while (intermediateParent != ancestor) { intermediateParent.applyPaintTransform(child, transform); - intermediateParent = intermediateParent.parent!; - child = child.parent!; + intermediateParent = intermediateParent.parent! as RenderObject; + child = child.parent! as RenderObject; assert(intermediateParent != null); } ancestor.applyPaintTransform(child, transform); diff --git a/packages/flutter/lib/src/rendering/viewport.dart b/packages/flutter/lib/src/rendering/viewport.dart index 11721c936c..01a69aedf2 100644 --- a/packages/flutter/lib/src/rendering/viewport.dart +++ b/packages/flutter/lib/src/rendering/viewport.dart @@ -43,7 +43,7 @@ abstract class RenderAbstractViewport extends RenderObject { while (object != null) { if (object is RenderAbstractViewport) return object; - object = object.parent; + object = object.parent as RenderObject?; } return null; } @@ -764,7 +764,7 @@ abstract class RenderViewportBase { final MultiChildLayoutParentData parentData = renderObject.parentData! as MultiChildLayoutParentData; if (parentData.id != id) { parentData.id = id; - final RenderObject? targetParent = renderObject.parent; - if (targetParent != null) + final AbstractNode? targetParent = renderObject.parent; + if (targetParent is RenderObject) targetParent.markNeedsLayout(); } } @@ -4132,8 +4132,8 @@ class Positioned extends ParentDataWidget { } if (needsLayout) { - final RenderObject? targetParent = renderObject.parent; - if (targetParent != null) + final AbstractNode? targetParent = renderObject.parent; + if (targetParent is RenderObject) targetParent.markNeedsLayout(); } } @@ -4992,8 +4992,8 @@ class Flexible extends ParentDataWidget { } if (needsLayout) { - final RenderObject? targetParent = renderObject.parent; - if (targetParent != null) + final AbstractNode? targetParent = renderObject.parent; + if (targetParent is RenderObject) targetParent.markNeedsLayout(); } } diff --git a/packages/flutter/lib/src/widgets/sliver.dart b/packages/flutter/lib/src/widgets/sliver.dart index b90e9464a3..226bea7c69 100644 --- a/packages/flutter/lib/src/widgets/sliver.dart +++ b/packages/flutter/lib/src/widgets/sliver.dart @@ -1747,8 +1747,8 @@ class KeepAlive extends ParentDataWidget { final KeepAliveParentDataMixin parentData = renderObject.parentData! as KeepAliveParentDataMixin; if (parentData.keepAlive != keepAlive) { parentData.keepAlive = keepAlive; - final RenderObject? targetParent = renderObject.parent; - if (targetParent != null && !keepAlive) + final AbstractNode? targetParent = renderObject.parent; + if (targetParent is RenderObject && !keepAlive) targetParent.markNeedsLayout(); // No need to redo layout if it became true. } } diff --git a/packages/flutter/lib/src/widgets/table.dart b/packages/flutter/lib/src/widgets/table.dart index c0c0199af7..3e32d3026e 100644 --- a/packages/flutter/lib/src/widgets/table.dart +++ b/packages/flutter/lib/src/widgets/table.dart @@ -433,8 +433,8 @@ class TableCell extends ParentDataWidget { final TableCellParentData parentData = renderObject.parentData! as TableCellParentData; if (parentData.verticalAlignment != verticalAlignment) { parentData.verticalAlignment = verticalAlignment; - final RenderObject? targetParent = renderObject.parent; - if (targetParent != null) + final AbstractNode? targetParent = renderObject.parent; + if (targetParent is RenderObject) targetParent.markNeedsLayout(); } } diff --git a/packages/flutter/lib/src/widgets/widget_inspector.dart b/packages/flutter/lib/src/widgets/widget_inspector.dart index 4e7dbe95ef..f22944ad61 100644 --- a/packages/flutter/lib/src/widgets/widget_inspector.dart +++ b/packages/flutter/lib/src/widgets/widget_inspector.dart @@ -552,7 +552,7 @@ class _ScreenshotPaintingContext extends PaintingContext { }) { RenderObject repaintBoundary = renderObject; while (repaintBoundary != null && !repaintBoundary.isRepaintBoundary) { - repaintBoundary = repaintBoundary.parent!; + repaintBoundary = repaintBoundary.parent! as RenderObject; } assert(repaintBoundary != null); final _ScreenshotData data = _ScreenshotData(target: renderObject); @@ -1510,7 +1510,7 @@ mixin WidgetInspectorService { final List chain = []; while (renderObject != null) { chain.add(renderObject); - renderObject = renderObject.parent; + renderObject = renderObject.parent as RenderObject?; } return _followDiagnosticableChain(chain.reversed.toList()); } @@ -2565,7 +2565,7 @@ class _RenderInspectorOverlay extends RenderBox { context.addLayer(_InspectorOverlayLayer( overlayRect: Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height), selection: selection, - rootRenderObject: parent != null ? parent! : null, + rootRenderObject: parent is RenderObject ? parent! as RenderObject : null, )); } } @@ -2841,14 +2841,14 @@ class _InspectorOverlayLayer extends Layer { /// overlays in the same app (i.e. an storyboard), a selected or candidate /// render object may not belong to this tree. bool _isInInspectorRenderObjectTree(RenderObject child) { - RenderObject? current = child.parent; + RenderObject? current = child.parent as RenderObject?; while (current != null) { // We found the widget inspector render object. if (current is RenderStack && current.lastChild is _RenderInspectorOverlay) { return rootRenderObject == current; } - current = current.parent; + current = current.parent as RenderObject?; } return false; } diff --git a/packages/flutter/test/material/tooltip_test.dart b/packages/flutter/test/material/tooltip_test.dart index d3a6a4d6ff..0114e669af 100644 --- a/packages/flutter/test/material/tooltip_test.dart +++ b/packages/flutter/test/material/tooltip_test.dart @@ -1941,5 +1941,5 @@ Future testGestureTap(WidgetTester tester, Finder tooltip) async { SemanticsNode findDebugSemantics(RenderObject object) { if (object.debugSemantics != null) return object.debugSemantics!; - return findDebugSemantics(object.parent!); + return findDebugSemantics(object.parent! as RenderObject); } diff --git a/packages/flutter/test/material/tooltip_theme_test.dart b/packages/flutter/test/material/tooltip_theme_test.dart index 7b4f681093..4d1eb407a0 100644 --- a/packages/flutter/test/material/tooltip_theme_test.dart +++ b/packages/flutter/test/material/tooltip_theme_test.dart @@ -1357,5 +1357,5 @@ void main() { SemanticsNode findDebugSemantics(RenderObject object) { if (object.debugSemantics != null) return object.debugSemantics!; - return findDebugSemantics(object.parent!); + return findDebugSemantics(object.parent! as RenderObject); } diff --git a/packages/flutter/test/rendering/cached_intrinsics_test.dart b/packages/flutter/test/rendering/cached_intrinsics_test.dart index bc42ef20c4..756a4967d5 100644 --- a/packages/flutter/test/rendering/cached_intrinsics_test.dart +++ b/packages/flutter/test/rendering/cached_intrinsics_test.dart @@ -118,7 +118,7 @@ void main() { expect(parentData!.offset.dy, -(viewHeight / 2.0)); expect(test.calls, 2); // The layout constraints change will clear the cached data. - final RenderObject parent = test.parent!; + final RenderObject parent = test.parent! as RenderObject; expect(parent.debugNeedsLayout, false); // Do not forget notify parent dirty after the cached data be cleared by `layout()` diff --git a/packages/flutter/test/rendering/editable_test.dart b/packages/flutter/test/rendering/editable_test.dart index 8d7a4387c3..f5f074a377 100644 --- a/packages/flutter/test/rendering/editable_test.dart +++ b/packages/flutter/test/rendering/editable_test.dart @@ -935,7 +935,7 @@ void main() { editable.painter = null; editable.paintCount = 0; - final RenderObject? parent = editable.parent; + final AbstractNode? parent = editable.parent; if (parent is RenderConstrainedBox) parent.child = null; }); diff --git a/packages/flutter/test/rendering/non_render_object_root_test.dart b/packages/flutter/test/rendering/non_render_object_root_test.dart new file mode 100644 index 0000000000..f5d33d3b25 --- /dev/null +++ b/packages/flutter/test/rendering/non_render_object_root_test.dart @@ -0,0 +1,61 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'rendering_tester.dart'; + +class RealRoot extends AbstractNode { + RealRoot(this.child) { + adoptChild(child); + } + + final RenderObject child; + + @override + void redepthChildren() { + redepthChild(child); + } + + @override + void attach(Object owner) { + super.attach(owner); + child.attach(owner as PipelineOwner); + } + + @override + void detach() { + super.detach(); + child.detach(); + } + + @override + PipelineOwner? get owner => super.owner as PipelineOwner?; + + void layout() { + child.layout(BoxConstraints.tight(const Size(500.0, 500.0))); + } +} + +void main() { + TestRenderingFlutterBinding.ensureInitialized(); + + test('non-RenderObject roots', () { + RenderPositionedBox child; + final RealRoot root = RealRoot( + child = RenderPositionedBox( + child: RenderSizedBox(const Size(100.0, 100.0)), + ), + ); + root.attach(PipelineOwner()); + + child.scheduleInitialLayout(); + root.layout(); + + child.markNeedsLayout(); + root.layout(); + }); +} diff --git a/packages/flutter/test/rendering/proxy_box_test.dart b/packages/flutter/test/rendering/proxy_box_test.dart index 21b1cd9eae..12831d9671 100644 --- a/packages/flutter/test/rendering/proxy_box_test.dart +++ b/packages/flutter/test/rendering/proxy_box_test.dart @@ -689,9 +689,9 @@ void main() { test('Offstage implements paintsChild correctly', () { final RenderBox box = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20)); - final RenderProxyBox parent = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20)); + final RenderBox parent = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20)); final RenderOffstage offstage = RenderOffstage(offstage: false, child: box); - parent.child = offstage; + parent.adoptChild(offstage); expect(offstage.paintsChild(box), true); @@ -702,9 +702,9 @@ void main() { test('Opacity implements paintsChild correctly', () { final RenderBox box = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20)); - final RenderProxyBox parent = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20)); + final RenderBox parent = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20)); final RenderOpacity opacity = RenderOpacity(child: box); - parent.child = opacity; + parent.adoptChild(opacity); expect(opacity.paintsChild(box), true); @@ -714,11 +714,11 @@ void main() { }); test('AnimatedOpacity sets paint matrix to zero when alpha == 0', () { - final RenderProxyBox box = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20)); - final RenderProxyBox parent = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20)); + final RenderBox box = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20)); + final RenderBox parent = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20)); final AnimationController opacityAnimation = AnimationController(value: 1, vsync: FakeTickerProvider()); final RenderAnimatedOpacity opacity = RenderAnimatedOpacity(opacity: opacityAnimation, child: box); - parent.child = opacity; + parent.adoptChild(opacity); // Make it listen to the animation. opacity.attach(PipelineOwner()); @@ -732,10 +732,10 @@ void main() { test('AnimatedOpacity sets paint matrix to zero when alpha == 0 (sliver)', () { final RenderSliver sliver = RenderSliverToBoxAdapter(child: RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20))); - final RenderSliverPadding parent = RenderSliverPadding(padding: const EdgeInsets.all(4)); + final RenderBox parent = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20)); final AnimationController opacityAnimation = AnimationController(value: 1, vsync: FakeTickerProvider()); final RenderSliverAnimatedOpacity opacity = RenderSliverAnimatedOpacity(opacity: opacityAnimation, sliver: sliver); - parent.child = opacity; + parent.adoptChild(opacity); // Make it listen to the animation. opacity.attach(PipelineOwner()); diff --git a/packages/flutter/test/widgets/heroes_test.dart b/packages/flutter/test/widgets/heroes_test.dart index 92a7643b43..c4cbbaeb9f 100644 --- a/packages/flutter/test/widgets/heroes_test.dart +++ b/packages/flutter/test/widgets/heroes_test.dart @@ -2924,8 +2924,8 @@ Future main() async { final ScrollController controller = ScrollController(); RenderAnimatedOpacity? findRenderAnimatedOpacity() { - RenderObject? parent = tester.renderObject(find.byType(Placeholder)); - while (parent != null && parent is! RenderAnimatedOpacity) { + AbstractNode? parent = tester.renderObject(find.byType(Placeholder)); + while (parent is RenderObject && parent is! RenderAnimatedOpacity) { parent = parent.parent; } return parent is RenderAnimatedOpacity ? parent : null; diff --git a/packages/flutter_driver/lib/src/common/handler_factory.dart b/packages/flutter_driver/lib/src/common/handler_factory.dart index 3bf6d4fa77..6d283546ef 100644 --- a/packages/flutter_driver/lib/src/common/handler_factory.dart +++ b/packages/flutter_driver/lib/src/common/handler_factory.dart @@ -308,7 +308,7 @@ mixin CommandHandlerFactory { SemanticsNode? node; while (renderObject != null && node == null) { node = renderObject.debugSemantics; - renderObject = renderObject.parent; + renderObject = renderObject.parent as RenderObject?; } if (node == null) throw StateError('No semantics data found'); diff --git a/packages/flutter_test/lib/src/_matchers_io.dart b/packages/flutter_test/lib/src/_matchers_io.dart index ec877e7709..a8675d5ce9 100644 --- a/packages/flutter_test/lib/src/_matchers_io.dart +++ b/packages/flutter_test/lib/src/_matchers_io.dart @@ -23,7 +23,7 @@ Future captureImage(Element element) { assert(element.renderObject != null); RenderObject renderObject = element.renderObject!; while (!renderObject.isRepaintBoundary) { - renderObject = renderObject.parent!; + renderObject = renderObject.parent! as RenderObject; } assert(!renderObject.debugNeedsPaint); final OffsetLayer layer = renderObject.debugLayer! as OffsetLayer; diff --git a/packages/flutter_test/lib/src/_matchers_web.dart b/packages/flutter_test/lib/src/_matchers_web.dart index 0af6dbcc61..d2d769eafa 100644 --- a/packages/flutter_test/lib/src/_matchers_web.dart +++ b/packages/flutter_test/lib/src/_matchers_web.dart @@ -82,7 +82,7 @@ RenderObject _findRepaintBoundary(Element element) { assert(element.renderObject != null); RenderObject renderObject = element.renderObject!; while (!renderObject.isRepaintBoundary) { - renderObject = renderObject.parent!; + renderObject = renderObject.parent! as RenderObject; } return renderObject; } diff --git a/packages/flutter_test/lib/src/controller.dart b/packages/flutter_test/lib/src/controller.dart index e71c928127..0e0bfed851 100644 --- a/packages/flutter_test/lib/src/controller.dart +++ b/packages/flutter_test/lib/src/controller.dart @@ -1168,7 +1168,7 @@ abstract class WidgetController { RenderObject? renderObject = element.findRenderObject(); SemanticsNode? result = renderObject?.debugSemantics; while (renderObject != null && (result == null || result.isMergedIntoParent)) { - renderObject = renderObject.parent; + renderObject = renderObject.parent as RenderObject?; result = renderObject?.debugSemantics; } if (result == null)