Skip to content

Commit 5fbe2a9

Browse files
author
fbchen
committed
enhance constraints cache
1 parent 4dee3f1 commit 5fbe2a9

File tree

4 files changed

+80
-58
lines changed

4 files changed

+80
-58
lines changed

README.md

+8-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ No matter how complex the layout is and how deep the constraints are, it has alm
66
performance as Flex and Stack. When facing complex layouts, it provides better performance,
77
flexibility, and a very flat code hierarchy than Flex and Stack. Say no to 'nesting hell'.
88

9-
See [Flutter Web Online Example](https://constraintlayout.flutterfirst.cn)
9+
View [Flutter Web Online Example](https://constraintlayout.flutterfirst.cn)
1010

1111
**Flutter ConstraintLayout has extremely high layout performance because it does not require linear
1212
equations to solve. It is recommended to use ConstraintLayout at the top level. For extremely
@@ -563,9 +563,13 @@ class OffBuildExample extends StatelessWidget {
563563
wrapContent will be re-layout. And since the constraints passed to other child elements won't
564564
change, no real re-layout will be triggered.
565565

566-
4. Using preprocessed constraints can improve performance, especially when the ListView is swiping
567-
quickly, constraints are no longer calculated during layout. Need to be used in conjunction with
568-
childConstraints. see example/preprocess_complex_list.dart
566+
4. By default, constraints are not recalculated during layout if the constraints of child elements
567+
do not change. Even the constraint computation is extremely fast. But when the ListView is swiped
568+
quickly, constraints are calculated for each item layout process, even though the constraints of
569+
these items may not change. This is not necessary. At this point ChildConstraintsCache can be
570+
used to optimize it so that constraints for entries of the same type are computed only once.
571+
Refer to example/complex_list.dart. Constraints can also be calculated ahead of time so that they
572+
don't need to be calculated during layout. Refer to example/preprocess_complex_list.dart.
569573

570574
# Support me
571575

example/complex_list.dart

+5-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class ComplexListExample extends StatelessWidget {
2121
Colors.pink,
2222
];
2323

24+
ChildConstraintsCache childConstraintsCache = ChildConstraintsCache();
25+
2426
return Scaffold(
2527
appBar: const CustomAppBar(
2628
title: 'ComplexList',
@@ -29,7 +31,7 @@ class ComplexListExample extends StatelessWidget {
2931
itemBuilder: (context, index) {
3032
if (index == 0) {
3133
return const Text(
32-
'Very complex item view can also achieve full frame',
34+
'Very complex item view can also achieve full fps',
3335
style: TextStyle(
3436
color: Colors.black,
3537
fontSize: 25,
@@ -38,6 +40,8 @@ class ComplexListExample extends StatelessWidget {
3840
);
3941
}
4042
return ConstraintLayout(
43+
useCacheConstraints: true,
44+
childConstraintsCache: childConstraintsCache,
4145
children: [
4246
Container(
4347
color: colors[index % 6],

example/preprocess_complex_list.dart

+5-10
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,8 @@ class PreprocessComplexListExample extends StatelessWidget {
9090
),
9191
];
9292

93-
/// Using preprocessed constraints can improve performance, especially when
94-
/// the ListView is swiping quickly, constraints are no longer calculated during
95-
/// layout. Need to be used in conjunction with childConstraints.
96-
ProcessedChildConstraints processedChildConstraints =
97-
ConstraintLayout.preprocess(childConstraints);
93+
ChildConstraintsCache childConstraintsCache =
94+
ConstraintLayout.generateCache(childConstraints);
9895

9996
return Scaffold(
10097
appBar: const CustomAppBar(
@@ -104,9 +101,7 @@ class PreprocessComplexListExample extends StatelessWidget {
104101
itemBuilder: (context, index) {
105102
if (index == 0) {
106103
return const Text(
107-
"""Using preprocessed constraints can improve performance, especially when
108-
the ListView is swiping quickly, constraints are no longer calculated during
109-
layout. Need to be used in conjunction with childConstraints.""",
104+
"""Constraints can also be calculated ahead of time so that they don't need to be calculated during layout.""",
110105
style: TextStyle(
111106
color: Colors.black,
112107
fontSize: 16,
@@ -115,9 +110,9 @@ layout. Need to be used in conjunction with childConstraints.""",
115110
);
116111
}
117112
return ConstraintLayout(
118-
preprocessChildConstraints: true,
113+
useCacheConstraints: true,
119114
childConstraints: childConstraints,
120-
processedChildConstraints: processedChildConstraints,
115+
childConstraintsCache: childConstraintsCache,
121116
children: [
122117
Container(
123118
color: colors[index % 6],

lib/src/constraint_layout.dart

+62-43
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,18 @@ class ConstraintLayout extends MultiChildRenderObjectWidget {
2525
final String? debugName;
2626
final bool debugShowZIndex;
2727

28-
/// Using preprocessed constraints can improve performance, especially when
29-
/// the ListView is swiping quickly, constraints are no longer calculated during
30-
/// layout. Need to be used in conjunction with childConstraints.
31-
final ProcessedChildConstraints? processedChildConstraints;
32-
final bool preprocessChildConstraints;
28+
///By default, constraints are not recalculated during layout if the constraints of
29+
///child elements do not change. Even the constraint computation is extremely fast.
30+
//
31+
// But when the ListView is swiped quickly, constraints are calculated for each item
32+
// layout process, even though the constraints of these items may not change. This is
33+
// not necessary. At this point ChildConstraintsCache can be used to optimize it so that
34+
// constraints for entries of the same type are computed only once. Refer to example/complex_list.dart
35+
//
36+
// Constraints can also be calculated ahead of time so that they don't need to be
37+
// calculated during layout. Refer to example/preprocess_complex_list.dart
38+
final ChildConstraintsCache? childConstraintsCache;
39+
final bool useCacheConstraints;
3340

3441
ConstraintLayout({
3542
Key? key,
@@ -43,8 +50,8 @@ class ConstraintLayout extends MultiChildRenderObjectWidget {
4350
this.releasePrintLayoutTime = false,
4451
this.debugName,
4552
this.debugShowZIndex = false,
46-
this.preprocessChildConstraints = false,
47-
this.processedChildConstraints,
53+
this.useCacheConstraints = false,
54+
this.childConstraintsCache,
4855
}) : super(
4956
key: key,
5057
children: children,
@@ -53,8 +60,7 @@ class ConstraintLayout extends MultiChildRenderObjectWidget {
5360
@override
5461
RenderObject createRenderObject(BuildContext context) {
5562
assert(_debugEnsureNotEmptyString('debugName', debugName));
56-
assert(preprocessChildConstraints == false ||
57-
(processedChildConstraints != null));
63+
assert(useCacheConstraints == false || (childConstraintsCache != null));
5864
return _ConstraintRenderBox()
5965
.._childConstraints = childConstraints
6066
.._debugShowGuideline = debugShowGuideline
@@ -65,8 +71,8 @@ class ConstraintLayout extends MultiChildRenderObjectWidget {
6571
.._releasePrintLayoutTime = releasePrintLayoutTime
6672
.._debugName = debugName
6773
.._debugShowZIndex = debugShowZIndex
68-
.._preprocessChildConstraints = preprocessChildConstraints
69-
.._processedChildConstraints = processedChildConstraints;
74+
.._useCacheConstraints = useCacheConstraints
75+
.._childConstraintsCache = childConstraintsCache;
7076
}
7177

7278
@override
@@ -75,6 +81,7 @@ class ConstraintLayout extends MultiChildRenderObjectWidget {
7581
covariant RenderObject renderObject,
7682
) {
7783
assert(_debugEnsureNotEmptyString('debugName', debugName));
84+
assert(useCacheConstraints == false || (childConstraintsCache != null));
7885
(renderObject as _ConstraintRenderBox)
7986
..childConstraints = childConstraints
8087
..debugShowGuideline = debugShowGuideline
@@ -85,21 +92,20 @@ class ConstraintLayout extends MultiChildRenderObjectWidget {
8592
..releasePrintLayoutTime = releasePrintLayoutTime
8693
..debugName = debugName
8794
..debugShowZIndex = debugShowZIndex
88-
..preprocessChildConstraints = preprocessChildConstraints
89-
..processedChildConstraints = processedChildConstraints;
95+
..useCacheConstraints = useCacheConstraints
96+
..childConstraintsCache = childConstraintsCache;
9097
}
9198

92-
static ProcessedChildConstraints preprocess(
99+
static ChildConstraintsCache generateCache(
93100
List<Constraint> childConstraints) {
94-
ProcessedChildConstraints processedChildConstraints =
95-
ProcessedChildConstraints();
101+
ChildConstraintsCache processedChildConstraints = ChildConstraintsCache();
96102
processedChildConstraints._processedNodesMap = {};
97103

98104
_ConstrainedNode _getConstrainedNodeForChild(
99105
RenderBox? child,
100106
ConstraintId id,
101107
) {
102-
return processedChildConstraints._processedNodesMap
108+
return processedChildConstraints._processedNodesMap!
103109
.putIfAbsent(id, () => _ConstrainedNode()..nodeId = id);
104110
}
105111

@@ -237,13 +243,13 @@ class ConstraintLayout extends MultiChildRenderObjectWidget {
237243
currentNode.baselineAlignType = constraint.baseline!.type;
238244
}
239245

240-
processedChildConstraints._processedNodesMap[constraint.id!] =
246+
processedChildConstraints._processedNodesMap![constraint.id!] =
241247
currentNode;
242248
}
243249

244-
processedChildConstraints._processedNodesMap.remove(parent);
250+
processedChildConstraints._processedNodesMap!.remove(parent);
245251
processedChildConstraints._processedNodes =
246-
processedChildConstraints._processedNodesMap.values.toList();
252+
processedChildConstraints._processedNodesMap!.values.toList();
247253

248254
return processedChildConstraints;
249255
}
@@ -1341,12 +1347,11 @@ class _ConstraintRenderBox extends RenderBox
13411347
late bool _releasePrintLayoutTime;
13421348
String? _debugName;
13431349
late bool _debugShowZIndex;
1344-
late bool _preprocessChildConstraints;
1345-
ProcessedChildConstraints? _processedChildConstraints;
1350+
late bool _useCacheConstraints;
1351+
ChildConstraintsCache? _childConstraintsCache;
13461352

13471353
bool _needsRecalculateConstraints = true;
13481354
bool _needsReorderChildren = true;
1349-
final Map<RenderBox, _ConstrainedNode> _tempConstrainedNodeMap = HashMap();
13501355
final Map<ConstraintId, _ConstrainedNode> _constrainedNodeMap = HashMap();
13511356

13521357
/// For layout
@@ -1450,17 +1455,21 @@ class _ConstraintRenderBox extends RenderBox
14501455
}
14511456
}
14521457

1453-
set preprocessChildConstraints(bool value) {
1454-
if (_preprocessChildConstraints != value) {
1455-
_preprocessChildConstraints = value;
1458+
set useCacheConstraints(bool value) {
1459+
if (_useCacheConstraints != value) {
1460+
_useCacheConstraints = value;
1461+
if (!value && _childConstraintsCache != null) {
1462+
_childConstraintsCache!._processedNodes = null;
1463+
_childConstraintsCache!._processedNodesMap = null;
1464+
}
14561465
markNeedsRecalculateConstraints();
14571466
markNeedsLayout();
14581467
}
14591468
}
14601469

1461-
set processedChildConstraints(ProcessedChildConstraints? value) {
1462-
if (_processedChildConstraints != value) {
1463-
_processedChildConstraints = value;
1470+
set childConstraintsCache(ChildConstraintsCache? value) {
1471+
if (_childConstraintsCache != value) {
1472+
_childConstraintsCache = value;
14641473
markNeedsRecalculateConstraints();
14651474
markNeedsLayout();
14661475
}
@@ -1632,17 +1641,18 @@ class _ConstraintRenderBox extends RenderBox
16321641
id, () => _ConstrainedNode()..nodeId = id);
16331642
if (child != null && node.renderBox == null) {
16341643
node.renderBox = child;
1635-
_tempConstrainedNodeMap[child] = node;
16361644
}
16371645
return node;
16381646
}
16391647

16401648
void _buildConstrainedNodeTrees() {
1641-
_tempConstrainedNodeMap.clear();
16421649
_constrainedNodeMap.clear();
1650+
if (_useCacheConstraints) {
1651+
_childConstraintsCache!._processedNodes = [];
1652+
}
1653+
16431654
RenderBox? child = firstChild;
16441655
int childIndex = -1;
1645-
16461656
while (child != null) {
16471657
childIndex++;
16481658
_ConstraintBoxData childParentData =
@@ -1687,8 +1697,17 @@ class _ConstraintRenderBox extends RenderBox
16871697
currentNode.baselineAlignType = childParentData.baseline!.type;
16881698
}
16891699

1700+
if (_useCacheConstraints) {
1701+
_childConstraintsCache!._processedNodes!.add(currentNode);
1702+
}
1703+
16901704
child = childParentData.nextSibling;
16911705
}
1706+
1707+
_constrainedNodeMap.remove(parent);
1708+
if (_useCacheConstraints) {
1709+
_childConstraintsCache!._processedNodesMap = _constrainedNodeMap;
1710+
}
16921711
}
16931712

16941713
@override
@@ -1741,10 +1760,11 @@ class _ConstraintRenderBox extends RenderBox
17411760
return true;
17421761
}());
17431762

1744-
if (_preprocessChildConstraints) {
1763+
if (_useCacheConstraints &&
1764+
_childConstraintsCache!._processedNodes != null) {
17451765
List<_ConstrainedNode> layoutList = [];
17461766
List<_ConstrainedNode> paintList = [];
1747-
for (final element in _processedChildConstraints!._processedNodes) {
1767+
for (final element in _childConstraintsCache!._processedNodes!) {
17481768
_ConstrainedNode constrainedNode = _ConstrainedNode()
17491769
..nodeId = element.nodeId
17501770
..leftConstraint = element.leftConstraint
@@ -1769,7 +1789,7 @@ class _ConstraintRenderBox extends RenderBox
17691789
_ConstraintBoxData childParentData =
17701790
child.parentData as _ConstraintBoxData;
17711791
childParentData._constrainedNodeMap =
1772-
_processedChildConstraints!._processedNodesMap;
1792+
_childConstraintsCache!._processedNodesMap!;
17731793
_layoutOrderList[childIndex].parentData = childParentData;
17741794
_layoutOrderList[childIndex].index = childIndex;
17751795
_layoutOrderList[childIndex].renderBox = child;
@@ -1807,7 +1827,7 @@ class _ConstraintRenderBox extends RenderBox
18071827
_ConstraintBoxData childParentData =
18081828
child.parentData as _ConstraintBoxData;
18091829
childParentData._constrainedNodeMap =
1810-
_processedChildConstraints!._processedNodesMap;
1830+
_childConstraintsCache!._processedNodesMap!;
18111831
_paintingOrderList[childIndex].parentData = childParentData;
18121832
_paintingOrderList[childIndex].index = childIndex;
18131833
_paintingOrderList[childIndex].renderBox = child;
@@ -1831,17 +1851,16 @@ class _ConstraintRenderBox extends RenderBox
18311851
assert(() {
18321852
if (_debugCheckConstraints) {
18331853
List<_ConstrainedNode> nodeList =
1834-
_tempConstrainedNodeMap.values.toList();
1854+
_constrainedNodeMap.values.toList();
18351855
_debugCheckConstraintsIntegrity(nodeList);
18361856
_debugCheckLoopConstraints(nodeList);
18371857
}
18381858
return true;
18391859
}());
18401860

18411861
/// Sort by the depth of constraint from shallow to deep, the lowest depth is 0, representing parent
1842-
_layoutOrderList = _tempConstrainedNodeMap.values.toList();
1843-
_paintingOrderList = _tempConstrainedNodeMap.values.toList();
1844-
_tempConstrainedNodeMap.clear();
1862+
_layoutOrderList = _constrainedNodeMap.values.toList();
1863+
_paintingOrderList = _constrainedNodeMap.values.toList();
18451864

18461865
_layoutOrderList.sort((left, right) {
18471866
return left.getDepth() - right.getDepth();
@@ -2658,9 +2677,9 @@ class _ConstraintRenderBox extends RenderBox
26582677
}
26592678
}
26602679

2661-
class ProcessedChildConstraints {
2662-
late List<_ConstrainedNode> _processedNodes;
2663-
late Map<ConstraintId, _ConstrainedNode> _processedNodesMap;
2680+
class ChildConstraintsCache {
2681+
List<_ConstrainedNode>? _processedNodes;
2682+
Map<ConstraintId, _ConstrainedNode>? _processedNodesMap;
26642683
}
26652684

26662685
class _ConstrainedNode {

0 commit comments

Comments
 (0)