@@ -1853,7 +1853,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge
18531853 assert (child._parent == this );
18541854 assert (child.attached == attached);
18551855 assert (child.parentData != null );
1856- child. _cleanRelayoutBoundary ( );
1856+ _cleanChildRelayoutBoundary (child );
18571857 child.parentData! .detach ();
18581858 child.parentData = null ;
18591859 child._parent = null ;
@@ -2185,6 +2185,32 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge
21852185 }
21862186 bool _needsLayout = true ;
21872187
2188+ /// The nearest relayout boundary enclosing this render object, if known.
2189+ ///
2190+ /// When a render object is marked as needing layout, its parent may
2191+ /// as a result also need to be marked as needing layout.
2192+ /// For details, see [markNeedsLayout] .
2193+ /// A render object where relayout does not require relayout of the parent
2194+ /// (because its size cannot change on relayout, or because
2195+ /// its parent does not use the child's size for its own layout)
2196+ /// is a "relayout boundary".
2197+ ///
2198+ /// This property is set in [layout] , and consulted by [markNeedsLayout] in
2199+ /// deciding whether to recursively mark the parent as also needing layout.
2200+ ///
2201+ /// This property is initially null, and becomes null again if this
2202+ /// render object is removed from the tree (with [dropChild] );
2203+ /// it remains null until the first layout of this render object
2204+ /// after it was most recently added to the tree.
2205+ /// This property can also be null while an ancestor in the tree is
2206+ /// currently doing layout, until this render object itself does layout.
2207+ ///
2208+ /// When not null, the relayout boundary is either this render object itself
2209+ /// or one of its ancestors, and all the render objects in the ancestry chain
2210+ /// up through that ancestor have the same [_relayoutBoundary] .
2211+ /// Equivalently: when not null, the relayout boundary is either this render
2212+ /// object itself or the same as that of its parent. (So [_relayoutBoundary]
2213+ /// is one of `null` , `this` , or `parent!._relayoutBoundary!` .)
21882214 RenderObject ? _relayoutBoundary;
21892215
21902216 /// Whether [invokeLayoutCallback] for this render object is currently running.
@@ -2222,7 +2248,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge
22222248 /// (where it will always be false).
22232249 static bool debugCheckingIntrinsics = false ;
22242250
2225- bool _debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout () {
2251+ bool _debugRelayoutBoundaryAlreadyMarkedNeedsLayout () {
22262252 if (_relayoutBoundary == null ) {
22272253 // We don't know where our relayout boundary is yet.
22282254 return true ;
@@ -2281,7 +2307,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge
22812307 void markNeedsLayout () {
22822308 assert (_debugCanPerformMutations);
22832309 if (_needsLayout) {
2284- assert (_debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout ());
2310+ assert (_debugRelayoutBoundaryAlreadyMarkedNeedsLayout ());
22852311 return ;
22862312 }
22872313 if (_relayoutBoundary == null ) {
@@ -2346,32 +2372,36 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge
23462372 markParentNeedsLayout ();
23472373 }
23482374
2349- void _cleanRelayoutBoundary () {
2350- if (_relayoutBoundary != this ) {
2351- _relayoutBoundary = null ;
2352- visitChildren (_cleanChildRelayoutBoundary);
2375+ /// Set [_relayoutBoundary] to null throughout this render object's subtree,
2376+ /// stopping at relayout boundaries.
2377+ // This is a static method to reduce closure allocation with visitChildren.
2378+ static void _cleanChildRelayoutBoundary (RenderObject child) {
2379+ if (child._relayoutBoundary != child) {
2380+ child.visitChildren (_cleanChildRelayoutBoundary);
2381+ child._relayoutBoundary = null ;
23532382 }
23542383 }
23552384
2356- void _propagateRelayoutBoundary () {
2357- if (_relayoutBoundary == this ) {
2385+ // This is a static method to reduce closure allocation with visitChildren.
2386+ static void _propagateRelayoutBoundaryToChild (RenderObject child) {
2387+ if (child._relayoutBoundary == child) {
23582388 return ;
23592389 }
2360- final RenderObject ? parentRelayoutBoundary = parent? ._relayoutBoundary;
2390+ final RenderObject ? parentRelayoutBoundary = child. parent? ._relayoutBoundary;
23612391 assert (parentRelayoutBoundary != null );
2362- if (parentRelayoutBoundary != _relayoutBoundary) {
2363- _relayoutBoundary = parentRelayoutBoundary;
2364- visitChildren (_propagateRelayoutBoundaryToChild);
2365- }
2392+ assert (parentRelayoutBoundary != child._relayoutBoundary);
2393+ child._setRelayoutBoundary (parentRelayoutBoundary! );
23662394 }
23672395
2368- // Reduces closure allocation for visitChildren use cases.
2369- static void _cleanChildRelayoutBoundary (RenderObject child) {
2370- child._cleanRelayoutBoundary ();
2371- }
2372-
2373- static void _propagateRelayoutBoundaryToChild (RenderObject child) {
2374- child._propagateRelayoutBoundary ();
2396+ /// Set [_relayoutBoundary] to [value] throughout this render object's
2397+ /// subtree, including this render object but stopping at relayout boundaries
2398+ /// thereafter.
2399+ void _setRelayoutBoundary (RenderObject value) {
2400+ assert (value != _relayoutBoundary);
2401+ // This may temporarily break the _relayoutBoundary invariant at children;
2402+ // the visitChildren restores the invariant.
2403+ _relayoutBoundary = value;
2404+ visitChildren (_propagateRelayoutBoundaryToChild);
23752405 }
23762406
23772407 /// Bootstrap the rendering pipeline by scheduling the very first layout.
@@ -2396,6 +2426,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge
23962426
23972427 @pragma ('vm:notify-debugger-on-exception' )
23982428 void _layoutWithoutResize () {
2429+ assert (_needsLayout);
23992430 assert (_relayoutBoundary == this );
24002431 RenderObject ? debugPreviousActiveLayout;
24012432 assert (! _debugMutationsLocked);
@@ -2520,8 +2551,7 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge
25202551 }());
25212552
25222553 if (relayoutBoundary != _relayoutBoundary) {
2523- _relayoutBoundary = relayoutBoundary;
2524- visitChildren (_propagateRelayoutBoundaryToChild);
2554+ _setRelayoutBoundary (relayoutBoundary);
25252555 }
25262556
25272557 if (! kReleaseMode && debugProfileLayoutsEnabled) {
@@ -2530,13 +2560,15 @@ abstract class RenderObject with DiagnosticableTreeMixin implements HitTestTarge
25302560 return ;
25312561 }
25322562 _constraints = constraints;
2563+
25332564 if (_relayoutBoundary != null && relayoutBoundary != _relayoutBoundary) {
25342565 // The local relayout boundary has changed, must notify children in case
25352566 // they also need updating. Otherwise, they will be confused about what
25362567 // their actual relayout boundary is later.
25372568 visitChildren (_cleanChildRelayoutBoundary);
25382569 }
25392570 _relayoutBoundary = relayoutBoundary;
2571+
25402572 assert (! _debugMutationsLocked);
25412573 assert (! _doingThisLayoutWithCallback);
25422574 assert (() {
0 commit comments