@@ -459,6 +459,7 @@ extension ConstrainedWidgetsExt on Widget {
459459 double ? widthHeightRatio,
460460 bool ? ratioBaseOnWidth,
461461 int ? eIndex,
462+ PinnedInfo ? pinnedInfo,
462463 }) {
463464 return Constrained (
464465 key: key,
@@ -524,6 +525,7 @@ extension ConstrainedWidgetsExt on Widget {
524525 centerBottomCenterTo: centerBottomCenterTo,
525526 centerBottomRightTo: centerBottomRightTo,
526527 eIndex: eIndex,
528+ pinnedInfo: pinnedInfo,
527529 ),
528530 child: this ,
529531 );
@@ -934,6 +936,95 @@ class ConstraintDefine {
934936 int get hashCode => id.hashCode;
935937}
936938
939+ enum PinnedType {
940+ absolute,
941+ percent,
942+ }
943+
944+ class PinnedPos {
945+ double xOffset;
946+ PinnedType xType;
947+ double yOffset;
948+ PinnedType yType;
949+
950+ PinnedPos (this .xOffset, this .xType, this .yOffset, this .yType);
951+
952+ void checkBounds (double value, PinnedType pinnedType, double base ) {
953+ if (pinnedType == PinnedType .absolute) {
954+ assert (value >= 0 && value <= base );
955+ } else {
956+ assert (value >= 0 && value <= 1 );
957+ }
958+ }
959+
960+ Offset resolve (Size size) {
961+ assert (() {
962+ checkBounds (xOffset, xType, size.width);
963+ checkBounds (yOffset, yType, size.height);
964+ return true ;
965+ }());
966+ double x;
967+ double y;
968+ if (xType == PinnedType .absolute) {
969+ x = xOffset;
970+ } else {
971+ x = xOffset * size.width;
972+ }
973+ if (yType == PinnedType .absolute) {
974+ y = yOffset;
975+ } else {
976+ y = yOffset * size.height;
977+ }
978+ return Offset (x, y);
979+ }
980+
981+ @override
982+ bool operator == (Object other) =>
983+ identical (this , other) ||
984+ other is PinnedPos &&
985+ runtimeType == other.runtimeType &&
986+ xOffset == other.xOffset &&
987+ xType == other.xType &&
988+ yOffset == other.yOffset &&
989+ yType == other.yType;
990+
991+ @override
992+ int get hashCode =>
993+ xOffset.hashCode ^ xType.hashCode ^ yOffset.hashCode ^ yType.hashCode;
994+ }
995+
996+ class PinnedInfo {
997+ /// [0,360]
998+ int rotateDegree;
999+ ConstraintId anchorId;
1000+ PinnedPos selfPos;
1001+ PinnedPos targetPos;
1002+
1003+ PinnedInfo (
1004+ this .anchorId,
1005+ this .selfPos,
1006+ this .targetPos, {
1007+ this .rotateDegree = 0 ,
1008+ });
1009+
1010+ @override
1011+ bool operator == (Object other) =>
1012+ identical (this , other) ||
1013+ other is PinnedInfo &&
1014+ runtimeType == other.runtimeType &&
1015+ rotateDegree == other.rotateDegree &&
1016+ anchorId == other.anchorId &&
1017+ selfPos == other.selfPos &&
1018+ targetPos == other.targetPos;
1019+
1020+ @override
1021+ int get hashCode =>
1022+ rotateDegree.hashCode ^
1023+ anchorId.hashCode ^
1024+ selfPos.hashCode ^
1025+ targetPos.hashCode;
1026+ }
1027+
9371028class Constraint extends ConstraintDefine {
9381029 /// 'wrap_content'、'match_parent'、'match_constraint'、'48, etc'
9391030 /// 'match_parent' will be converted to the base constraints
@@ -1073,6 +1164,8 @@ class Constraint extends ConstraintDefine {
10731164
10741165 final int ? eIndex;
10751166
1167+ final PinnedInfo ? pinnedInfo;
1168+
10761169 Constraint ({
10771170 ConstraintId ? id,
10781171 this .width = wrapContent,
@@ -1135,6 +1228,7 @@ class Constraint extends ConstraintDefine {
11351228 this .widthHeightRatio,
11361229 this .ratioBaseOnWidth,
11371230 this .eIndex,
1231+ this .pinnedInfo,
11381232 }) : super (id);
11391233
11401234 @override
@@ -1201,7 +1295,8 @@ class Constraint extends ConstraintDefine {
12011295 maxHeight == other.maxHeight &&
12021296 widthHeightRatio == other.widthHeightRatio &&
12031297 ratioBaseOnWidth == other.ratioBaseOnWidth &&
1204- eIndex == other.eIndex;
1298+ eIndex == other.eIndex &&
1299+ pinnedInfo == other.pinnedInfo;
12051300
12061301 @override
12071302 int get hashCode =>
@@ -1264,7 +1359,8 @@ class Constraint extends ConstraintDefine {
12641359 maxHeight.hashCode ^
12651360 widthHeightRatio.hashCode ^
12661361 ratioBaseOnWidth.hashCode ^
1267- eIndex.hashCode;
1362+ eIndex.hashCode ^
1363+ pinnedInfo.hashCode;
12681364
12691365 bool checkSize (double size) {
12701366 if (size == matchParent || size == wrapContent || size == matchConstraint) {
@@ -1801,6 +1897,25 @@ class Constraint extends ConstraintDefine {
18011897 needsReorderEventOrder = true ;
18021898 }
18031899
1900+ if (parentData.pinnedInfo != pinnedInfo) {
1901+ if (parentData.pinnedInfo == null || pinnedInfo == null ) {
1902+ needsRecalculateConstraints = true ;
1903+ needsLayout = true ;
1904+ } else {
1905+ if (parentData.pinnedInfo! .anchorId != pinnedInfo! .anchorId) {
1906+ needsRecalculateConstraints = true ;
1907+ needsLayout = true ;
1908+ } else if (parentData.pinnedInfo! .selfPos != pinnedInfo! .selfPos ||
1909+ parentData.pinnedInfo! .targetPos != pinnedInfo! .targetPos) {
1910+ needsLayout = true ;
1911+ } else if (parentData.pinnedInfo! .rotateDegree !=
1912+ pinnedInfo! .rotateDegree) {
1913+ needsPaint = true ;
1914+ }
1915+ }
1916+ parentData.pinnedInfo = pinnedInfo;
1917+ }
1918+
18041919 if (needsLayout) {
18051920 AbstractNode ? targetParent = renderObject.parent;
18061921 if (needsRecalculateConstraints) {
@@ -1875,6 +1990,7 @@ class _ConstraintBoxData extends ContainerBoxParentData<RenderBox> {
18751990 double ? widthHeightRatio;
18761991 bool ? ratioBaseOnWidth;
18771992 int ? eIndex;
1993+ PinnedInfo ? pinnedInfo;
18781994
18791995 // for internal use
18801996 late Map <ConstraintId , _ConstrainedNode > _constrainedNodeMap;
@@ -2227,6 +2343,9 @@ class _ConstraintRenderBox extends RenderBox
22272343 if (child is _BarrierRenderBox ) {
22282344 constraintsIdSet.addAll (childParentData._referencedIds! );
22292345 }
2346+ if (childParentData.pinnedInfo != null ) {
2347+ constraintsIdSet.add (childParentData.pinnedInfo! .anchorId);
2348+ }
22302349 child = childParentData.nextSibling;
22312350 }
22322351
@@ -2265,6 +2384,18 @@ class _ConstraintRenderBox extends RenderBox
22652384 /// Each child element must have complete constraints both horizontally and vertically
22662385 static void _debugCheckConstraintsIntegrity (List <_ConstrainedNode > nodeList) {
22672386 for (final element in nodeList) {
2387+ if (element.pinnedInfo != null ) {
2388+ if (element.width != wrapContent && element.width < 0 ) {
2389+ throw ConstraintLayoutException (
2390+ 'When setting pinnedInfo, width and height must be wrapContent or fixed size.' );
2391+ }
2392+ if (element.height != wrapContent && element.height < 0 ) {
2393+ throw ConstraintLayoutException (
2394+ 'When setting pinnedInfo, width and height must be wrapContent or fixed size.' );
2395+ }
2396+ continue ;
2397+ }
2398+
22682399 /// Check constraint integrity in the horizontal direction
22692400 if (element.width == wrapContent || element.width >= 0 ) {
22702401 if (element.leftConstraint == null && element.rightConstraint == null ) {
@@ -2492,6 +2623,11 @@ class _ConstraintRenderBox extends RenderBox
24922623 currentNode.baselineAlignType = childParentData.baseline! .type;
24932624 }
24942625
2626+ if (childParentData.pinnedInfo != null ) {
2627+ currentNode.pinnedConstraint = _getConstrainedNodeForChild (
2628+ childParentData.pinnedInfo! .anchorId, childIndex);
2629+ }
2630+
24952631 child = childParentData.nextSibling;
24962632 }
24972633
@@ -2814,7 +2950,7 @@ class _ConstraintRenderBox extends RenderBox
28142950 } else if (sizeConfirmedChild.rightConstraint != null ) {
28152951 childSpanWidth += size.width - sizeConfirmedChild.getRight ();
28162952 } else {
2817- /// It is not possible to execute this branch
2953+ childSpanWidth += sizeConfirmedChild. getX ();
28182954 }
28192955
28202956 if (sizeConfirmedChild.topConstraint != null &&
@@ -2824,7 +2960,7 @@ class _ConstraintRenderBox extends RenderBox
28242960 } else if (sizeConfirmedChild.bottomConstraint != null ) {
28252961 childSpanHeight += size.height - sizeConfirmedChild.getBottom ();
28262962 } else {
2827- /// It is not possible to execute this branch
2963+ childSpanHeight += sizeConfirmedChild. getY ();
28282964 }
28292965
28302966 if (childSpanWidth > contentWidth) {
@@ -3190,6 +3326,18 @@ class _ConstraintRenderBox extends RenderBox
31903326 }
31913327
31923328 Offset calculateChildOffset (_ConstrainedNode node) {
3329+ if (node.pinnedInfo != null ) {
3330+ PinnedInfo pinnedInfo = node.pinnedInfo! ;
3331+ Offset selfOffset = pinnedInfo.selfPos.resolve (node.renderBox! .size);
3332+ Offset targetOffset =
3333+ pinnedInfo.targetPos.resolve (node.pinnedConstraint! .getSize (this ));
3334+ double offsetX =
3335+ node.pinnedConstraint! .getX () + targetOffset.dx - selfOffset.dx;
3336+ double offsetY =
3337+ node.pinnedConstraint! .getY () + targetOffset.dy - selfOffset.dy;
3338+ return Offset (offsetX, offsetY);
3339+ }
3340+
31933341 EdgeInsets margin = node.margin;
31943342 EdgeInsets goneMargin = node.goneMargin;
31953343 double offsetX = 0 ;
@@ -3730,6 +3878,7 @@ class _ConstrainedNode {
37303878 _ConstrainedNode ? rightConstraint;
37313879 _ConstrainedNode ? bottomConstraint;
37323880 _ConstrainedNode ? baselineConstraint;
3881+ _ConstrainedNode ? pinnedConstraint;
37333882 _AlignType ? leftAlignType;
37343883 _AlignType ? topAlignType;
37353884 _AlignType ? rightAlignType;
@@ -3823,6 +3972,8 @@ class _ConstrainedNode {
38233972
38243973 Size ? get helperSize => parentData._helperSize;
38253974
3975+ PinnedInfo ? get pinnedInfo => parentData.pinnedInfo;
3976+
38263977 set helperSize (Size ? size) {
38273978 parentData._helperSize = size;
38283979 }
@@ -3886,6 +4037,13 @@ class _ConstrainedNode {
38864037 return getY () + getMeasuredHeight ();
38874038 }
38884039
4040+ Size getSize ([RenderBox ? parent]) {
4041+ if (isParent ()) {
4042+ return parent! .size;
4043+ }
4044+ return renderBox! .size;
4045+ }
4046+
38894047 double getMeasuredWidth () {
38904048 if (isGuideline || isBarrier) {
38914049 return helperSize! .width;
@@ -3975,6 +4133,9 @@ class _ConstrainedNode {
39754133 if (baselineConstraint != null )
39764134 getDepthFor (baselineConstraint! , parentSizeConfirmed, resolvedWidth,
39774135 resolvedHeight),
4136+ if (pinnedConstraint != null )
4137+ getDepthFor (pinnedConstraint! , parentSizeConfirmed, resolvedWidth,
4138+ resolvedHeight),
39784139 ];
39794140 depth = getMaxDepth (list) + 1 ;
39804141 }
@@ -4051,6 +4212,13 @@ class _ConstrainedNode {
40514212 map['baselineConstraint' ] = baselineConstraint! .toJson ();
40524213 }
40534214 }
4215+ if (pinnedConstraint != null ) {
4216+ if (pinnedConstraint! .isParent ()) {
4217+ map['pinnedConstraint' ] = 'parent' ;
4218+ } else {
4219+ map['pinnedConstraint' ] = pinnedConstraint! .toJson ();
4220+ }
4221+ }
40544222 }
40554223 map['depth' ] = getDepth (null , null , null );
40564224 return map;
@@ -4090,6 +4258,7 @@ class _HelperBox extends RenderBox {
40904258 constraintBoxData.widthHeightRatio = null ;
40914259 constraintBoxData.ratioBaseOnWidth = null ;
40924260 constraintBoxData.eIndex = null ;
4261+ constraintBoxData.pinnedInfo = null ;
40934262 constraintBoxData._direction = null ;
40944263 constraintBoxData._referencedIds = null ;
40954264 constraintBoxData._isGuideline = false ;
0 commit comments