Revert "Remove AbstractNode from RenderObject and deprecate it (#128973)"

This reverts commit 5ab5d82a39bfedc1ae82a13d01c23615d4e3b5d9.
diff --git a/packages/flutter/lib/src/cupertino/dialog.dart b/packages/flutter/lib/src/cupertino/dialog.dart
index 0cb4cf8..1f9e0e9 100644
--- a/packages/flutter/lib/src/cupertino/dialog.dart
+++ b/packages/flutter/lib/src/cupertino/dialog.dart
@@ -1556,7 +1556,7 @@
       parentData.isPressed = isPressed;
 
       // Force a repaint.
-      final RenderObject? targetParent = renderObject.parent;
+      final AbstractNode? targetParent = renderObject.parent;
       if (targetParent is RenderObject) {
         targetParent.markNeedsPaint();
       }
diff --git a/packages/flutter/lib/src/foundation/node.dart b/packages/flutter/lib/src/foundation/node.dart
index ca383fb..6e830f1 100644
--- a/packages/flutter/lib/src/foundation/node.dart
+++ b/packages/flutter/lib/src/foundation/node.dart
@@ -8,10 +8,6 @@
 // during device lab performance tests. When editing this file, check to make sure
 // that it didn't break that test.
 
-/// Deprecated. Unused by the framework and will be removed in a future version
-/// of Flutter. If needed, inline any required functionality of this class
-/// directly in the subclass.
-///
 /// An abstract node in a tree.
 ///
 /// AbstractNode has as notion of depth, attachment, and parent, but does not
@@ -43,10 +39,6 @@
 /// moved to be a child of A, sibling of B, then the numbers won't change. C's
 /// [depth] will still be 2. The [depth] is automatically maintained by the
 /// [adoptChild] and [dropChild] methods.
-@Deprecated(
-  'If needed, inline any required functionality of AbstractNode in your class directly. '
-  'This feature was deprecated after v3.12.0-4.0.pre.',
-)
 class AbstractNode {
   /// The depth of this node in the tree.
   ///
diff --git a/packages/flutter/lib/src/material/data_table.dart b/packages/flutter/lib/src/material/data_table.dart
index b1c01ce..970292c 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';
 
@@ -1201,7 +1202,7 @@
   RectCallback getRectCallback(RenderBox referenceBox) {
     return () {
       RenderObject cell = referenceBox;
-      RenderObject? table = cell.parent;
+      AbstractNode? table = cell.parent;
       final Matrix4 transform = Matrix4.identity();
       while (table is RenderObject && table is! RenderTable) {
         table.applyPaintTransform(cell, transform);
diff --git a/packages/flutter/lib/src/material/material.dart b/packages/flutter/lib/src/material/material.dart
index e7d76a4..5d8ccf4 100644
--- a/packages/flutter/lib/src/material/material.dart
+++ b/packages/flutter/lib/src/material/material.dart
@@ -762,7 +762,7 @@
       final int toDepth = to.depth;
 
       if (fromDepth >= toDepth) {
-        final RenderObject? fromParent = from.parent;
+        final AbstractNode? fromParent = from.parent;
         // Return early if the 2 render objects are not in the same render tree,
         // or either of them is offscreen and thus won't get painted.
         if (fromParent is! RenderObject || !fromParent.paintsChild(from)) {
@@ -773,7 +773,7 @@
       }
 
       if (fromDepth <= toDepth) {
-        final RenderObject? toParent = to.parent;
+        final AbstractNode? toParent = to.parent;
         if (toParent is! RenderObject || !toParent.paintsChild(to)) {
           return null;
         }
diff --git a/packages/flutter/lib/src/rendering/box.dart b/packages/flutter/lib/src/rendering/box.dart
index e89ad00..9b9a7c4 100644
--- a/packages/flutter/lib/src/rendering/box.dart
+++ b/packages/flutter/lib/src/rendering/box.dart
@@ -2136,6 +2136,7 @@
     assert(!_debugDoingBaseline, 'Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.');
     assert(!debugNeedsLayout);
     assert(() {
+      final RenderObject? parent = this.parent as RenderObject?;
       if (owner!.debugDoingLayout) {
         return (RenderObject.debugActiveLayout == parent) && parent!.debugDoingThisLayout;
       }
@@ -2143,6 +2144,7 @@
         return ((RenderObject.debugActivePaint == parent) && parent!.debugDoingThisPaint) ||
                ((RenderObject.debugActivePaint == this) && debugDoingThisPaint);
       }
+      assert(parent == this.parent);
       return false;
     }());
     assert(_debugSetDoingBaseline(true));
diff --git a/packages/flutter/lib/src/rendering/list_wheel_viewport.dart b/packages/flutter/lib/src/rendering/list_wheel_viewport.dart
index 48bc762..8296910 100644
--- a/packages/flutter/lib/src/rendering/list_wheel_viewport.dart
+++ b/packages/flutter/lib/src/rendering/list_wheel_viewport.dart
@@ -1133,7 +1133,7 @@
     // `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;
diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart
index b1984b4..1f73177 100644
--- a/packages/flutter/lib/src/rendering/object.dart
+++ b/packages/flutter/lib/src/rendering/object.dart
@@ -929,9 +929,11 @@
   }
 
   /// The unique object managed by this pipeline that has no parent.
-  RenderObject? get rootNode => _rootNode;
-  RenderObject? _rootNode;
-  set rootNode(RenderObject? value) {
+  ///
+  /// This object does not have to be a [RenderObject].
+  AbstractNode? get rootNode => _rootNode;
+  AbstractNode? _rootNode;
+  set rootNode(AbstractNode? value) {
     if (_rootNode == value) {
       return;
     }
@@ -1570,7 +1572,7 @@
 /// [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() {
     if (kFlutterMemoryAllocationsEnabled) {
@@ -1688,93 +1690,30 @@
     }
   }
 
-  /// The depth of this node in the tree.
-  ///
-  /// The depth of nodes in a tree monotonically increases as you traverse down
-  /// the tree.
-  ///
-  /// Nodes always have a [depth] greater than their ancestors'. There's no
-  /// guarantee regarding depth between siblings. The depth of a node is used to
-  /// ensure that nodes are processed in depth order. The [depth] of a child can
-  /// be more than one greater than the [depth] of the parent, because the [depth]
-  /// values are never decreased: all that matters is that it's greater than the
-  /// parent. Consider a tree with a root node A, a child B, and a grandchild C.
-  /// Initially, A will have [depth] 0, B [depth] 1, and C [depth] 2. If C is
-  /// moved to be a child of A, sibling of B, then the numbers won't change. C's
-  /// [depth] will still be 2. The [depth] is automatically maintained by the
-  /// [adoptChild] and [dropChild] methods.
-  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.
-  @protected
-  void redepthChildren() { }
-
-  /// 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.
-  @mustCallSuper
-  @protected
+  @override
   void adoptChild(RenderObject child) {
-    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;
-    }());
-
     setupParentData(child);
     markNeedsLayout();
     markNeedsCompositingBitsUpdate();
     markNeedsSemanticsUpdate();
-    child._parent = this;
-    if (attached) {
-      child.attach(_owner!);
-    }
-    redepthChild(child);
+    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.
-  @mustCallSuper
-  @protected
+  @override
   void dropChild(RenderObject child) {
-    assert(child._parent == this);
-    assert(child.attached == attached);
     assert(child.parentData != null);
     child._cleanRelayoutBoundary();
     child.parentData!.detach();
     child.parentData = null;
-    child._parent = null;
-    if (attached) {
-      child.detach();
-    }
+    super.dropChild(child);
     markNeedsLayout();
     markNeedsCompositingBitsUpdate();
     markNeedsSemanticsUpdate();
@@ -1912,7 +1851,7 @@
         }
 
         if (!activeLayoutRoot._debugMutationsLocked) {
-          final RenderObject? p = activeLayoutRoot.debugLayoutParent;
+          final AbstractNode? p = activeLayoutRoot.debugLayoutParent;
           activeLayoutRoot = p is RenderObject ? p : null;
         } else {
           // activeLayoutRoot found.
@@ -2007,41 +1946,20 @@
   RenderObject? get debugLayoutParent {
     RenderObject? layoutParent;
     assert(() {
-      layoutParent = parent;
+      final AbstractNode? parent = this.parent;
+      layoutParent = parent is RenderObject? ? parent : null;
       return true;
     }());
     return layoutParent;
   }
 
-  /// 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;
+  @override
+  PipelineOwner? get owner => super.owner as PipelineOwner?;
 
-  /// 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
+  @override
   void attach(PipelineOwner owner) {
     assert(!_debugDisposed);
-    assert(_owner == null);
-    _owner = owner;
+    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) {
@@ -2068,23 +1986,6 @@
     }
   }
 
-  /// 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);
-  }
-
   /// Whether this render object's layout information is dirty.
   ///
   /// This is only set in debug mode. In general, render objects should not need
@@ -2149,7 +2050,7 @@
     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;
       }
@@ -2243,7 +2144,7 @@
     assert(_debugCanPerformMutations);
     _needsLayout = true;
     assert(this.parent != null);
-    final RenderObject parent = this.parent!;
+    final RenderObject parent = this.parent! as RenderObject;
     if (!_doingThisLayoutWithCallback) {
       parent.markNeedsLayout();
     } else {
@@ -2275,7 +2176,7 @@
     if (_relayoutBoundary == this) {
       return;
     }
-    final RenderObject? parentRelayoutBoundary = parent?._relayoutBoundary;
+    final RenderObject? parentRelayoutBoundary = (parent as RenderObject?)?._relayoutBoundary;
     assert(parentRelayoutBoundary != null);
     if (parentRelayoutBoundary != _relayoutBoundary) {
       _relayoutBoundary = parentRelayoutBoundary;
@@ -2416,7 +2317,7 @@
     assert(!_debugDoingThisResize);
     assert(!_debugDoingThisLayout);
     final bool isRelayoutBoundary = !parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject;
-    final RenderObject relayoutBoundary = isRelayoutBoundary ? this : parent!._relayoutBoundary!;
+    final RenderObject relayoutBoundary = isRelayoutBoundary ? this : (parent! as RenderObject)._relayoutBoundary!;
     assert(() {
       _debugCanParentUseSize = parentUsesSize;
       return true;
@@ -2773,7 +2674,7 @@
     }
     _needsCompositingBitsUpdate = true;
     if (parent is RenderObject) {
-      final RenderObject parent = this.parent!;
+      final RenderObject parent = this.parent! as RenderObject;
       if (parent._needsCompositingBitsUpdate) {
         return;
       }
@@ -2922,7 +2823,9 @@
         owner!.requestVisualUpdate();
       }
     } else if (parent is RenderObject) {
-      parent!.markNeedsPaint();
+      final RenderObject parent = this.parent! as RenderObject;
+      parent.markNeedsPaint();
+      assert(parent == this.parent);
     } else {
       assert(() {
         if (debugPrintMarkNeedsPaintStacks) {
@@ -2993,7 +2896,7 @@
     assert(_needsPaint || _needsCompositedLayerUpdate);
     assert(_layerHandle.layer != null);
     assert(!_layerHandle.layer!.attached);
-    RenderObject? node = parent;
+    AbstractNode? node = parent;
     while (node is RenderObject) {
       if (node.isRepaintBoundary) {
         if (node._layerHandle.layer == null) {
@@ -3090,7 +2993,7 @@
     assert(() {
       if (_needsCompositingBitsUpdate) {
         if (parent is RenderObject) {
-          final RenderObject parent = this.parent!;
+          final RenderObject parent = this.parent! as RenderObject;
           bool visitedByParent = false;
           parent.visitChildren((RenderObject child) {
             if (child == this) {
@@ -3253,13 +3156,13 @@
     final bool ancestorSpecified = ancestor != null;
     assert(attached);
     if (ancestor == null) {
-      final RenderObject? rootNode = owner!.rootNode;
+      final AbstractNode? rootNode = owner!.rootNode;
       if (rootNode is RenderObject) {
         ancestor = rootNode;
       }
     }
     final List<RenderObject> renderers = <RenderObject>[];
-    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.
     }
@@ -3391,7 +3294,8 @@
     if (_semantics != null && !_semantics!.isMergedIntoParent) {
       _semantics!.sendEvent(semanticsEvent);
     } else if (parent != null) {
-      parent!.sendSemanticsEvent(semanticsEvent);
+      final RenderObject renderParent = parent! as RenderObject;
+      renderParent.sendSemanticsEvent(semanticsEvent);
     }
   }
 
@@ -3491,7 +3395,7 @@
         mayProduceSiblingNodes = false;
       }
 
-      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.
@@ -3774,9 +3678,9 @@
       }
       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';
@@ -3878,7 +3782,8 @@
     Curve curve = Curves.ease,
   }) {
     if (parent is RenderObject) {
-      parent!.showOnScreen(
+      final RenderObject renderParent = parent! as RenderObject;
+      renderParent.showOnScreen(
         descendant: descendant ?? this,
         rect: rect,
         duration: duration,
@@ -5065,11 +4970,11 @@
     Matrix4 clipRectTransform,
   ) {
     assert(clipRectTransform.isIdentity());
-    RenderObject intermediateParent = child.parent!;
+    RenderObject intermediateParent = child.parent! as RenderObject;
     while (intermediateParent != ancestor) {
       intermediateParent.applyPaintTransform(child, transform);
-      intermediateParent = intermediateParent.parent!;
-      child = child.parent!;
+      intermediateParent = intermediateParent.parent! as RenderObject;
+      child = child.parent! as RenderObject;
     }
     ancestor.applyPaintTransform(child, transform);
     ancestor.applyPaintTransform(child, clipRectTransform);
diff --git a/packages/flutter/lib/src/rendering/viewport.dart b/packages/flutter/lib/src/rendering/viewport.dart
index 44876d8..bf07a88 100644
--- a/packages/flutter/lib/src/rendering/viewport.dart
+++ b/packages/flutter/lib/src/rendering/viewport.dart
@@ -45,7 +45,7 @@
       if (object is RenderAbstractViewport) {
         return object;
       }
-      object = object.parent;
+      object = object.parent as RenderObject?;
     }
     return null;
   }
@@ -779,7 +779,7 @@
     RenderBox? pivot;
     bool onlySlivers = target is RenderSliver; // ... between viewport and `target` (`target` included).
     while (child.parent != this) {
-      final RenderObject parent = child.parent!;
+      final RenderObject parent = child.parent! as RenderObject;
       if (child is RenderBox) {
         pivot = child;
       }
@@ -1205,8 +1205,7 @@
     } else {
       // `descendant` is between leading and trailing edge and hence already
       //  fully shown on screen. No action necessary.
-      assert(viewport.parent != null);
-      final Matrix4 transform = descendant.getTransformTo(viewport.parent);
+      final Matrix4 transform = descendant.getTransformTo(viewport.parent! as RenderObject);
       return MatrixUtils.transformRect(transform, rect ?? descendant.paintBounds);
     }
 
diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart
index cd6c205..8be37bf 100644
--- a/packages/flutter/lib/src/widgets/basic.dart
+++ b/packages/flutter/lib/src/widgets/basic.dart
@@ -2263,7 +2263,7 @@
     final MultiChildLayoutParentData parentData = renderObject.parentData! as MultiChildLayoutParentData;
     if (parentData.id != id) {
       parentData.id = id;
-      final RenderObject? targetParent = renderObject.parent;
+      final AbstractNode? targetParent = renderObject.parent;
       if (targetParent is RenderObject) {
         targetParent.markNeedsLayout();
       }
@@ -4348,7 +4348,7 @@
     }
 
     if (needsLayout) {
-      final RenderObject? targetParent = renderObject.parent;
+      final AbstractNode? targetParent = renderObject.parent;
       if (targetParent is RenderObject) {
         targetParent.markNeedsLayout();
       }
@@ -5207,7 +5207,7 @@
     }
 
     if (needsLayout) {
-      final RenderObject? targetParent = renderObject.parent;
+      final AbstractNode? targetParent = renderObject.parent;
       if (targetParent is RenderObject) {
         targetParent.markNeedsLayout();
       }
diff --git a/packages/flutter/lib/src/widgets/overlay.dart b/packages/flutter/lib/src/widgets/overlay.dart
index 8a432ad..aad2536 100644
--- a/packages/flutter/lib/src/widgets/overlay.dart
+++ b/packages/flutter/lib/src/widgets/overlay.dart
@@ -936,14 +936,6 @@
   @override
   void redepthChildren() => visitChildren(redepthChild);
 
-  void _adoptDeferredLayoutBoxChild(_RenderDeferredLayoutBox child) {
-    adoptChild(child);
-  }
-
-  void _dropDeferredLayoutBoxChild(_RenderDeferredLayoutBox child) {
-    dropChild(child);
-  }
-
   Alignment? _alignmentCache;
   Alignment get _resolvedAlignment => _alignmentCache ??= AlignmentDirectional.topStart.resolve(textDirection);
 
@@ -1714,13 +1706,13 @@
   void _activate(_RenderDeferredLayoutBox child) {
     assert(_debugNotDisposed());
     assert(_overlayChildRenderBox == null, '$_overlayChildRenderBox');
-    _theater._adoptDeferredLayoutBoxChild(child);
+    _theater.adoptChild(child);
     _overlayChildRenderBox = child;
   }
 
   void _deactivate(_RenderDeferredLayoutBox child) {
     assert(_debugNotDisposed());
-    _theater._dropDeferredLayoutBoxChild(child);
+    _theater.dropChild(child);
     _overlayChildRenderBox = null;
   }
 
@@ -1988,7 +1980,7 @@
 
   @override
   _RenderTheater get theater {
-    final RenderObject? parent = this.parent;
+    final AbstractNode? parent = this.parent;
     return parent is _RenderTheater
       ? parent
       : throw FlutterError('$parent of $this is not a _RenderTheater');
diff --git a/packages/flutter/lib/src/widgets/sliver.dart b/packages/flutter/lib/src/widgets/sliver.dart
index 0090198..46b42cf 100644
--- a/packages/flutter/lib/src/widgets/sliver.dart
+++ b/packages/flutter/lib/src/widgets/sliver.dart
@@ -1342,7 +1342,7 @@
     if (parentData.keepAlive != keepAlive) {
       // No need to redo layout if it became true.
       parentData.keepAlive = keepAlive;
-      final RenderObject? targetParent = renderObject.parent;
+      final AbstractNode? targetParent = renderObject.parent;
       if (targetParent is RenderObject && !keepAlive) {
         targetParent.markNeedsLayout();
       }
@@ -1436,7 +1436,7 @@
     }
 
     if (needsLayout) {
-      final RenderObject? targetParent = renderObject.parent;
+      final AbstractNode? targetParent = renderObject.parent;
       if (targetParent is RenderObject) {
         targetParent.markNeedsLayout();
       }
@@ -1508,7 +1508,7 @@
     }
 
     if (needsLayout) {
-      final RenderObject? targetParent = renderObject.parent;
+      final AbstractNode? targetParent = renderObject.parent;
       if (targetParent is RenderObject) {
         targetParent.markNeedsLayout();
       }
diff --git a/packages/flutter/lib/src/widgets/table.dart b/packages/flutter/lib/src/widgets/table.dart
index a3aba1c..83d54be 100644
--- a/packages/flutter/lib/src/widgets/table.dart
+++ b/packages/flutter/lib/src/widgets/table.dart
@@ -417,7 +417,7 @@
     final TableCellParentData parentData = renderObject.parentData! as TableCellParentData;
     if (parentData.verticalAlignment != verticalAlignment) {
       parentData.verticalAlignment = verticalAlignment;
-      final RenderObject? targetParent = renderObject.parent;
+      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 8cb610f..7321181 100644
--- a/packages/flutter/lib/src/widgets/widget_inspector.dart
+++ b/packages/flutter/lib/src/widgets/widget_inspector.dart
@@ -553,7 +553,7 @@
   }) {
     RenderObject repaintBoundary = renderObject;
     while (!repaintBoundary.isRepaintBoundary) {
-      repaintBoundary = repaintBoundary.parent!;
+      repaintBoundary = repaintBoundary.parent! as RenderObject;
     }
     final _ScreenshotData data = _ScreenshotData(target: renderObject);
     final _ScreenshotPaintingContext context = _ScreenshotPaintingContext(
@@ -1632,7 +1632,7 @@
     final List<RenderObject> chain = <RenderObject>[];
     while (renderObject != null) {
       chain.add(renderObject);
-      renderObject = renderObject.parent;
+      renderObject = renderObject.parent as RenderObject?;
     }
     return _followDiagnosticableChain(chain.reversed.toList());
   }
@@ -2058,7 +2058,7 @@
             'renderObject': renderObject.toDiagnosticsNode().toJsonMap(renderObjectSerializationDelegate),
           };
 
-          final RenderObject? renderParent = renderObject.parent;
+          final AbstractNode? renderParent = renderObject.parent;
           if (renderParent is RenderObject && subtreeDepth > 0) {
             final Object? parentCreator = renderParent.debugCreator;
             if (parentCreator is DebugCreator) {
@@ -2955,7 +2955,7 @@
     context.addLayer(_InspectorOverlayLayer(
       overlayRect: Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height),
       selection: selection,
-      rootRenderObject: parent is RenderObject ? parent! : null,
+      rootRenderObject: parent is RenderObject ? parent! as RenderObject : null,
     ));
   }
 }
@@ -3244,14 +3244,14 @@
   /// 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 08140ff..f46cef7 100644
--- a/packages/flutter/test/material/tooltip_test.dart
+++ b/packages/flutter/test/material/tooltip_test.dart
@@ -2283,5 +2283,5 @@
   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 b2961b6..c658dd5 100644
--- a/packages/flutter/test/material/tooltip_theme_test.dart
+++ b/packages/flutter/test/material/tooltip_theme_test.dart
@@ -1334,5 +1334,5 @@
   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 bc42ef2..756a496 100644
--- a/packages/flutter/test/rendering/cached_intrinsics_test.dart
+++ b/packages/flutter/test/rendering/cached_intrinsics_test.dart
@@ -118,7 +118,7 @@
     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 6f2f74a..8093fe6 100644
--- a/packages/flutter/test/rendering/editable_test.dart
+++ b/packages/flutter/test/rendering/editable_test.dart
@@ -1006,7 +1006,7 @@
       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 0000000..f5d33d3
--- /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 38fe9a0..13991f1 100644
--- a/packages/flutter/test/rendering/proxy_box_test.dart
+++ b/packages/flutter/test/rendering/proxy_box_test.dart
@@ -803,10 +803,10 @@
   });
 
   test('Offstage implements paintsChild correctly', () {
-    final RenderConstrainedBox box = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20));
-    final RenderConstrainedBox 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 RenderOffstage offstage = RenderOffstage(offstage: false, child: box);
-    parent.child = offstage;
+    parent.adoptChild(offstage);
 
     expect(offstage.paintsChild(box), true);
 
@@ -817,7 +817,9 @@
 
   test('Opacity implements paintsChild correctly', () {
     final RenderBox box = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20));
+    final RenderBox parent = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 20));
     final RenderOpacity opacity = RenderOpacity(child: box);
+    parent.adoptChild(opacity);
 
     expect(opacity.paintsChild(box), true);
 
@@ -828,8 +830,10 @@
 
   test('AnimatedOpacity sets paint matrix to zero when alpha == 0', () {
     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.adoptChild(opacity);
 
     // Make it listen to the animation.
     opacity.attach(PipelineOwner());
@@ -843,8 +847,10 @@
 
   test('AnimatedOpacity sets paint matrix to zero when alpha == 0 (sliver)', () {
     final RenderSliver sliver = RenderSliverToBoxAdapter(child: 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 RenderSliverAnimatedOpacity opacity = RenderSliverAnimatedOpacity(opacity: opacityAnimation, sliver: sliver);
+    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 463f5ba..5dbc959 100644
--- a/packages/flutter/test/widgets/heroes_test.dart
+++ b/packages/flutter/test/widgets/heroes_test.dart
@@ -1188,7 +1188,7 @@
         if (currentNode is RenderAnimatedOpacity && currentNode.opacity.value == 0) {
           return false;
         }
-        currentNode = currentNode.parent;
+        currentNode = currentNode.parent as RenderObject?;
       }
       return true;
     }
@@ -2919,7 +2919,7 @@
     final ScrollController controller = ScrollController();
 
     RenderAnimatedOpacity? findRenderAnimatedOpacity() {
-      RenderObject? parent = tester.renderObject(find.byType(Placeholder));
+      AbstractNode? parent = tester.renderObject(find.byType(Placeholder));
       while (parent is RenderObject && parent is! RenderAnimatedOpacity) {
         parent = parent.parent;
       }
diff --git a/packages/flutter/test/widgets/overlay_portal_test.dart b/packages/flutter/test/widgets/overlay_portal_test.dart
index e280aeb..85b1a71 100644
--- a/packages/flutter/test/widgets/overlay_portal_test.dart
+++ b/packages/flutter/test/widgets/overlay_portal_test.dart
@@ -2,6 +2,7 @@
 // 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/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
@@ -28,7 +29,7 @@
 void rebuildLayoutBuilderSubtree(RenderBox descendant) {
   assert(descendant is! RenderConstrainedLayoutBuilder<BoxConstraints, RenderBox>);
 
-  RenderObject? node = descendant.parent;
+  AbstractNode? node = descendant.parent;
   while (node != null) {
     if (node is! RenderConstrainedLayoutBuilder<BoxConstraints, RenderBox>) {
       node = node.parent;
@@ -73,7 +74,7 @@
     if (node.runtimeType.toString() == '_RenderTheater') {
       results.add(node);
     }
-    final RenderObject? parent = node.parent;
+    final AbstractNode? parent = node.parent;
     node = parent is RenderObject? parent : null;
   }
   return results;
@@ -1524,14 +1525,14 @@
       final List<RenderObject> childrenVisited = <RenderObject>[];
       theater.visitChildren(childrenVisited.add);
       expect(childrenVisited.length, 3);
-      expect(childrenVisited, containsAllInOrder(<RenderObject>[child1Box.parent!, child2Box.parent!]));
+      expect(childrenVisited, containsAllInOrder(<AbstractNode>[child1Box.parent!, child2Box.parent!]));
       childrenVisited.clear();
 
       setState(() { reparented = true; });
       await tester.pump();
       theater.visitChildren(childrenVisited.add);
       // The child list stays the same.
-      expect(childrenVisited, containsAllInOrder(<RenderObject>[child1Box.parent!, child2Box.parent!]));
+      expect(childrenVisited, containsAllInOrder(<AbstractNode>[child1Box.parent!, child2Box.parent!]));
     });
   });
 }
diff --git a/packages/flutter_driver/lib/src/common/handler_factory.dart b/packages/flutter_driver/lib/src/common/handler_factory.dart
index 05e70ae..def0c66 100644
--- a/packages/flutter_driver/lib/src/common/handler_factory.dart
+++ b/packages/flutter_driver/lib/src/common/handler_factory.dart
@@ -320,7 +320,7 @@
     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 fb479a6..9c56372 100644
--- a/packages/flutter_test/lib/src/_matchers_io.dart
+++ b/packages/flutter_test/lib/src/_matchers_io.dart
@@ -24,7 +24,7 @@
   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 c671390..71a78c3 100644
--- a/packages/flutter_test/lib/src/_matchers_web.dart
+++ b/packages/flutter_test/lib/src/_matchers_web.dart
@@ -93,7 +93,7 @@
   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 4033ba0..bdc1ab9 100644
--- a/packages/flutter_test/lib/src/controller.dart
+++ b/packages/flutter_test/lib/src/controller.dart
@@ -87,7 +87,7 @@
     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) {
@@ -366,7 +366,7 @@
     final RenderObject object = element.renderObject!;
     RenderObject current = object;
     while (current.debugLayer == null) {
-      current = current.parent!;
+      current = current.parent! as RenderObject;
     }
     final ContainerLayer layer = current.debugLayer!;
     return _walkLayers(layer);