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());
|
assert(_debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout());
|
||||||
return;
|
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) {
|
if (_relayoutBoundary != this) {
|
||||||
markParentNeedsLayout();
|
markParentNeedsLayout();
|
||||||
} else {
|
} else {
|
||||||
@ -1683,16 +1692,31 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
|
|||||||
void _cleanRelayoutBoundary() {
|
void _cleanRelayoutBoundary() {
|
||||||
if (_relayoutBoundary != this) {
|
if (_relayoutBoundary != this) {
|
||||||
_relayoutBoundary = null;
|
_relayoutBoundary = null;
|
||||||
_needsLayout = true;
|
|
||||||
visitChildren(_cleanChildRelayoutBoundary);
|
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.
|
// Reduces closure allocation for visitChildren use cases.
|
||||||
static void _cleanChildRelayoutBoundary(RenderObject child) {
|
static void _cleanChildRelayoutBoundary(RenderObject child) {
|
||||||
child._cleanRelayoutBoundary();
|
child._cleanRelayoutBoundary();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _propagateRelayoutBoundaryToChild(RenderObject child) {
|
||||||
|
child._propagateRelayoutBoundary();
|
||||||
|
}
|
||||||
|
|
||||||
/// Bootstrap the rendering pipeline by scheduling the very first layout.
|
/// Bootstrap the rendering pipeline by scheduling the very first layout.
|
||||||
///
|
///
|
||||||
/// Requires this render object to be attached and that this render object
|
/// 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(!_debugDoingThisResize);
|
||||||
assert(!_debugDoingThisLayout);
|
assert(!_debugDoingThisLayout);
|
||||||
RenderObject? relayoutBoundary;
|
final bool isRelayoutBoundary = !parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject;
|
||||||
if (!parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject) {
|
final RenderObject relayoutBoundary = isRelayoutBoundary ? this : (parent! as RenderObject)._relayoutBoundary!;
|
||||||
relayoutBoundary = this;
|
|
||||||
} else {
|
|
||||||
relayoutBoundary = (parent! as RenderObject)._relayoutBoundary;
|
|
||||||
}
|
|
||||||
assert(() {
|
assert(() {
|
||||||
_debugCanParentUseSize = parentUsesSize;
|
_debugCanParentUseSize = parentUsesSize;
|
||||||
return true;
|
return true;
|
||||||
}());
|
}());
|
||||||
if (!_needsLayout && constraints == _constraints && relayoutBoundary == _relayoutBoundary) {
|
|
||||||
|
if (!_needsLayout && constraints == _constraints) {
|
||||||
assert(() {
|
assert(() {
|
||||||
// in case parentUsesSize changed since the last invocation, set size
|
// in case parentUsesSize changed since the last invocation, set size
|
||||||
// to itself, so it has the right internal debug values.
|
// to itself, so it has the right internal debug values.
|
||||||
@ -1839,6 +1860,11 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
|
|||||||
return true;
|
return true;
|
||||||
}());
|
}());
|
||||||
|
|
||||||
|
if (relayoutBoundary != _relayoutBoundary) {
|
||||||
|
_relayoutBoundary = relayoutBoundary;
|
||||||
|
visitChildren(_propagateRelayoutBoundaryToChild);
|
||||||
|
}
|
||||||
|
|
||||||
if (!kReleaseMode && debugProfileLayoutsEnabled)
|
if (!kReleaseMode && debugProfileLayoutsEnabled)
|
||||||
Timeline.finishSync();
|
Timeline.finishSync();
|
||||||
return;
|
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