Reland "Avoid calling performLayout
when only the relayout boundary is different" (#100581)
This commit is contained in:
parent
2268aebbae
commit
a63ee24b17
@ -1629,7 +1629,16 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
|
||||
assert(_debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout());
|
||||
return;
|
||||
}
|
||||
assert(_relayoutBoundary != null);
|
||||
if (_relayoutBoundary == null) {
|
||||
_needsLayout = true;
|
||||
if (parent != null) {
|
||||
// _relayoutBoundary is cleaned by an ancestor in RenderObject.layout.
|
||||
// Conservatively mark everything dirty until it reaches the closest
|
||||
// known relayout boundary.
|
||||
markParentNeedsLayout();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (_relayoutBoundary != this) {
|
||||
markParentNeedsLayout();
|
||||
} else {
|
||||
@ -1683,16 +1692,31 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
|
||||
void _cleanRelayoutBoundary() {
|
||||
if (_relayoutBoundary != this) {
|
||||
_relayoutBoundary = null;
|
||||
_needsLayout = true;
|
||||
visitChildren(_cleanChildRelayoutBoundary);
|
||||
}
|
||||
}
|
||||
|
||||
void _propagateRelayoutBoundary() {
|
||||
if (_relayoutBoundary == this) {
|
||||
return;
|
||||
}
|
||||
final RenderObject? parentRelayoutBoundary = (parent as RenderObject?)?._relayoutBoundary;
|
||||
assert(parentRelayoutBoundary != null);
|
||||
if (parentRelayoutBoundary != _relayoutBoundary) {
|
||||
_relayoutBoundary = parentRelayoutBoundary;
|
||||
visitChildren(_propagateRelayoutBoundaryToChild);
|
||||
}
|
||||
}
|
||||
|
||||
// Reduces closure allocation for visitChildren use cases.
|
||||
static void _cleanChildRelayoutBoundary(RenderObject child) {
|
||||
child._cleanRelayoutBoundary();
|
||||
}
|
||||
|
||||
static void _propagateRelayoutBoundaryToChild(RenderObject child) {
|
||||
child._propagateRelayoutBoundary();
|
||||
}
|
||||
|
||||
/// Bootstrap the rendering pipeline by scheduling the very first layout.
|
||||
///
|
||||
/// Requires this render object to be attached and that this render object
|
||||
@ -1814,17 +1838,14 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
|
||||
));
|
||||
assert(!_debugDoingThisResize);
|
||||
assert(!_debugDoingThisLayout);
|
||||
RenderObject? relayoutBoundary;
|
||||
if (!parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject) {
|
||||
relayoutBoundary = this;
|
||||
} else {
|
||||
relayoutBoundary = (parent! as RenderObject)._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;
|
||||
}());
|
||||
if (!_needsLayout && constraints == _constraints && relayoutBoundary == _relayoutBoundary) {
|
||||
|
||||
if (!_needsLayout && constraints == _constraints) {
|
||||
assert(() {
|
||||
// in case parentUsesSize changed since the last invocation, set size
|
||||
// to itself, so it has the right internal debug values.
|
||||
@ -1839,6 +1860,11 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
|
||||
return true;
|
||||
}());
|
||||
|
||||
if (relayoutBoundary != _relayoutBoundary) {
|
||||
_relayoutBoundary = relayoutBoundary;
|
||||
visitChildren(_propagateRelayoutBoundaryToChild);
|
||||
}
|
||||
|
||||
if (!kReleaseMode && debugProfileLayoutsEnabled)
|
||||
Timeline.finishSync();
|
||||
return;
|
||||
|
61
packages/flutter/test/rendering/relayout_boundary_test.dart
Normal file
61
packages/flutter/test/rendering/relayout_boundary_test.dart
Normal file
@ -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/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('relayout boundary change does not trigger relayout', (WidgetTester tester) async {
|
||||
final RenderLayoutCount renderLayoutCount = RenderLayoutCount();
|
||||
final Widget layoutCounter = Center(
|
||||
key: GlobalKey(),
|
||||
child: WidgetToRenderBoxAdapter(renderBox: renderLayoutCount),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: Center(
|
||||
child: layoutCounter,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(renderLayoutCount.layoutCount, 1);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: layoutCounter,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(renderLayoutCount.layoutCount, 1);
|
||||
});
|
||||
}
|
||||
|
||||
// This class is needed because LayoutBuilder's RenderObject does not always
|
||||
// call the builder method in its PerformLayout method.
|
||||
class RenderLayoutCount extends RenderBox {
|
||||
int layoutCount = 0;
|
||||
|
||||
@override
|
||||
void performLayout() {
|
||||
layoutCount += 1;
|
||||
size = constraints.biggest;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user