@@ -185,6 +185,7 @@ extension ConstrainedWidgetsExt on Widget {
185185 double minHeight = 0 ,
186186 double maxHeight = matchParent,
187187 double ? widthHeightRatio,
188+ bool ? ratioBaseOnWidth,
188189 }) {
189190 return Constrained (
190191 key: key,
@@ -231,6 +232,7 @@ extension ConstrainedWidgetsExt on Widget {
231232 minHeight: minHeight,
232233 maxHeight: maxHeight,
233234 widthHeightRatio: widthHeightRatio,
235+ ratioBaseOnWidth: ratioBaseOnWidth,
234236 ),
235237 child: this ,
236238 );
@@ -526,6 +528,12 @@ class Constraint {
526528 /// inferred (fixed size, matchParent, matchConstraint with two constraints)
527529 final double ? widthHeightRatio;
528530
531+ /// By default, ConstraintLayout will automatically decide which side to base on and
532+ /// calculate the size of the other side based on widthHeightRatio. But if both sides
533+ /// are matchConstraint, it cannot be determined automatically. At this point, you need
534+ /// to specify the ratioBaseOnWidth parameter. The default value of null means automatically decide
535+ final bool ? ratioBaseOnWidth;
536+
529537 Constraint ({
530538 this .id,
531539 this .width = wrapContent,
@@ -569,6 +577,7 @@ class Constraint {
569577 this .minHeight = 0 ,
570578 this .maxHeight = matchParent,
571579 this .widthHeightRatio,
580+ this .ratioBaseOnWidth,
572581 });
573582
574583 @override
@@ -616,7 +625,8 @@ class Constraint {
616625 maxWidth == other.maxWidth &&
617626 minHeight == other.minHeight &&
618627 maxHeight == other.maxHeight &&
619- widthHeightRatio == other.widthHeightRatio;
628+ widthHeightRatio == other.widthHeightRatio &&
629+ ratioBaseOnWidth == other.ratioBaseOnWidth;
620630
621631 @override
622632 int get hashCode =>
@@ -660,7 +670,8 @@ class Constraint {
660670 maxWidth.hashCode ^
661671 minHeight.hashCode ^
662672 maxHeight.hashCode ^
663- widthHeightRatio.hashCode;
673+ widthHeightRatio.hashCode ^
674+ ratioBaseOnWidth.hashCode;
664675
665676 bool checkSize (double size) {
666677 if (size == matchParent || size == wrapContent || size == matchConstraint) {
@@ -1009,6 +1020,11 @@ class Constraint {
10091020 needsLayout = true ;
10101021 }
10111022
1023+ if (parentData.ratioBaseOnWidth != ratioBaseOnWidth) {
1024+ parentData.ratioBaseOnWidth = ratioBaseOnWidth;
1025+ needsLayout = true ;
1026+ }
1027+
10121028 if (needsLayout) {
10131029 AbstractNode ? targetParent = renderObject.parent;
10141030 if (needsRecalculateConstraints) {
@@ -1075,6 +1091,7 @@ class _ConstraintBoxData extends ContainerBoxParentData<RenderBox> {
10751091 double ? minHeight;
10761092 double ? maxHeight;
10771093 double ? widthHeightRatio;
1094+ bool ? ratioBaseOnWidth;
10781095
10791096 // for internal use
10801097 late Map <ConstraintId , _ConstrainedNode > _constrainedNodeMap;
@@ -1412,8 +1429,13 @@ class _ConstraintRenderBox extends RenderBox
14121429
14131430 if (element.widthHeightRatio != null ) {
14141431 if (element.widthIsExact && element.heightIsExact) {
1415- throw ConstraintLayoutException (
1416- 'When setting widthHeightRatio for ${element .nodeId }, full constraints cannot be set on both sides at the same time.' );
1432+ if (element.width == matchConstraint &&
1433+ element.height == matchConstraint) {
1434+ if (element.ratioBaseOnWidth == null ) {
1435+ throw ConstraintLayoutException (
1436+ 'When setting widthHeightRatio for ${element .nodeId }, ratioBaseOnWidth is required.' );
1437+ }
1438+ }
14171439 } else if (! element.widthIsExact && ! element.heightIsExact) {
14181440 throw ConstraintLayoutException (
14191441 'When setting widthHeightRatio for ${element .nodeId }, one side needs full constraints.' );
@@ -1715,7 +1737,9 @@ class _ConstraintRenderBox extends RenderBox
17151737 }());
17161738 maxWidth = minWidth;
17171739 } else if (width == matchConstraint) {
1718- if (element.widthHeightRatio != null && ! element.widthIsExact) {
1740+ if (element.widthHeightRatio != null &&
1741+ element.heightIsExact &&
1742+ element.ratioBaseOnWidth != true ) {
17191743 /// The width needs to be calculated later based on the height
17201744 element.widthBasedHeight = true ;
17211745 minWidth = 0 ;
@@ -1797,7 +1821,9 @@ class _ConstraintRenderBox extends RenderBox
17971821 }());
17981822 maxHeight = minHeight;
17991823 } else if (height == matchConstraint) {
1800- if (element.widthHeightRatio != null && element.widthIsExact) {
1824+ if (element.widthHeightRatio != null &&
1825+ element.widthIsExact &&
1826+ element.ratioBaseOnWidth != false ) {
18011827 /// The height needs to be calculated later based on the width
18021828 /// minWidth == maxWidth
18031829 minHeight = minWidth / element.widthHeightRatio! ;
@@ -2471,6 +2497,8 @@ class _ConstrainedNode {
24712497
24722498 double ? get widthHeightRatio => parentData.widthHeightRatio;
24732499
2500+ bool ? get ratioBaseOnWidth => parentData.ratioBaseOnWidth;
2501+
24742502 /// fixed size, matchParent, matchConstraint with two constraints
24752503 bool get widthIsExact =>
24762504 width >= 0 ||
@@ -2707,6 +2735,7 @@ class _InternalBox extends RenderBox {
27072735 constraintBoxData.minHeight = 0 ;
27082736 constraintBoxData.maxHeight = matchParent;
27092737 constraintBoxData.widthHeightRatio = null ;
2738+ constraintBoxData.ratioBaseOnWidth = null ;
27102739 }
27112740}
27122741
0 commit comments