1+ import 'dart:math' as math;
2+
13import 'package:codelessly_api/codelessly_api.dart' ;
24import 'package:flutter/material.dart' ;
35
@@ -59,6 +61,7 @@ class PassiveRowColumnTransformer extends NodeWidgetTransformer<RowColumnNode> {
5961 final SizeFit mainFit = isRow
6062 ? rowColumnNode.horizontalFit
6163 : rowColumnNode.verticalFit;
64+ final bool wraps = rowColumnWraps (rowColumnNode);
6265 final bool crossWraps = isRow
6366 ? rowColumnNode.isVerticalWrap
6467 : rowColumnNode.isHorizontalWrap;
@@ -77,7 +80,9 @@ class PassiveRowColumnTransformer extends NodeWidgetTransformer<RowColumnNode> {
7780 rowColumnNode.crossAxisAlignment == CrossAxisAlignmentC .stretch;
7881 bool measureCross = false ;
7982 bool stretchFallsBack = false ;
80- if (crossMin == null && crossWraps) {
83+ // A Wrap has no stretch, and its children don't align across the run
84+ // (see [wrapChildWithSizeFits]), so it never needs measuring.
85+ if (crossMin == null && crossWraps && ! wraps) {
8186 final bool anyChildNeedsAlignment = childrenNodes.any (
8287 (child) => child.alignment != AlignmentModel .none,
8388 );
@@ -90,24 +95,41 @@ class PassiveRowColumnTransformer extends NodeWidgetTransformer<RowColumnNode> {
9095 }
9196 }
9297
93- Widget res = CodelesslyFlex (
94- direction: isRow ? Axis .horizontal : Axis .vertical,
95- mainAxisAlignment: rowColumnNode.mainAxisAlignment.flutterAxis,
96- crossAxisAlignment: stretchFallsBack
97- ? CrossAxisAlignment .start
98- : rowColumnNode.crossAxisAlignment.flutterAxis,
99- mainAxisSize: mainFit == SizeFit .shrinkWrap
100- ? MainAxisSize .min
101- : MainAxisSize .max,
102- key: ValueKey (rowColumnNode.id),
103- children: childrenWidgets,
104- );
98+ Widget res = wraps
99+ ? Wrap (
100+ key: ValueKey (rowColumnNode.id),
101+ direction: isRow ? Axis .horizontal : Axis .vertical,
102+ alignment: wrapAlignment (rowColumnNode.mainAxisAlignment),
103+ crossAxisAlignment: wrapCrossAlignment (
104+ rowColumnNode.crossAxisAlignment,
105+ ),
106+ runAlignment: wrapRunAlignment (rowColumnNode.crossAxisAlignment),
107+ spacing: rowColumnSpacing (rowColumnNode),
108+ runSpacing: (rowColumnNode as RowColumnNode ).runSpacing,
109+ children: childrenWidgets,
110+ )
111+ : CodelesslyFlex (
112+ direction: isRow ? Axis .horizontal : Axis .vertical,
113+ mainAxisAlignment: rowColumnNode.mainAxisAlignment.flutterAxis,
114+ crossAxisAlignment: stretchFallsBack
115+ ? CrossAxisAlignment .start
116+ : rowColumnNode.crossAxisAlignment.flutterAxis,
117+ mainAxisSize: mainFit == SizeFit .shrinkWrap
118+ ? MainAxisSize .min
119+ : MainAxisSize .max,
120+ spacing: rowColumnSpacing (rowColumnNode),
121+ key: ValueKey (rowColumnNode.id),
122+ children: childrenWidgets,
123+ );
105124
106125 if (measureCross) {
107126 res = isRow ? IntrinsicHeight (child: res) : IntrinsicWidth (child: res);
108127 }
109128
129+ // A wrapping row/column flows onto new runs instead of scrolling along
130+ // its main axis, so it never scrolls.
110131 if (withScroll &&
132+ ! wraps &&
111133 rowColumnNode is ScrollableMixin &&
112134 (rowColumnNode as ScrollableMixin ).isScrollable) {
113135 res = wrapWithPaddedScrollable (
@@ -146,6 +168,93 @@ class PassiveRowColumnTransformer extends NodeWidgetTransformer<RowColumnNode> {
146168 );
147169 }
148170
171+ /// Whether [node] is a row/column whose children flow onto new runs when
172+ /// they don't fit ([RowColumnNode.wrap] ), built as a [Wrap] .
173+ static bool rowColumnWraps (BaseNode node) =>
174+ node is RowColumnNode && node.wrap;
175+
176+ /// The main-axis gap between [node] 's children ([RowColumnNode.spacing] ).
177+ static double rowColumnSpacing (BaseNode node) =>
178+ node is RowColumnNode ? math.max (0 , node.spacing) : 0 ;
179+
180+ /// The [Wrap.alignment] of a wrapping row/column: how each run places its
181+ /// children along the main axis, as [MainAxisAlignmentC] does for a Flex.
182+ static WrapAlignment wrapAlignment (MainAxisAlignmentC alignment) =>
183+ switch (alignment) {
184+ MainAxisAlignmentC .start => WrapAlignment .start,
185+ MainAxisAlignmentC .center => WrapAlignment .center,
186+ MainAxisAlignmentC .end => WrapAlignment .end,
187+ MainAxisAlignmentC .spaceBetween => WrapAlignment .spaceBetween,
188+ MainAxisAlignmentC .spaceAround => WrapAlignment .spaceAround,
189+ MainAxisAlignmentC .spaceEvenly => WrapAlignment .spaceEvenly,
190+ };
191+
192+ /// The [Wrap.crossAxisAlignment] of a wrapping row/column: how children
193+ /// sit within their run. A Wrap can't stretch or align baselines; those
194+ /// place children at the start of the run.
195+ static WrapCrossAlignment wrapCrossAlignment (CrossAxisAlignmentC alignment) =>
196+ switch (alignment) {
197+ CrossAxisAlignmentC .center => WrapCrossAlignment .center,
198+ CrossAxisAlignmentC .end => WrapCrossAlignment .end,
199+ CrossAxisAlignmentC .start ||
200+ CrossAxisAlignmentC .stretch ||
201+ CrossAxisAlignmentC .baseline => WrapCrossAlignment .start,
202+ };
203+
204+ /// The [Wrap.runAlignment] of a wrapping row/column. The runs follow the
205+ /// cross-axis alignment too, so a wrapping row whose children fit on one
206+ /// run places them like the same row without wrapping.
207+ static WrapAlignment wrapRunAlignment (CrossAxisAlignmentC alignment) =>
208+ switch (alignment) {
209+ CrossAxisAlignmentC .center => WrapAlignment .center,
210+ CrossAxisAlignmentC .end => WrapAlignment .end,
211+ CrossAxisAlignmentC .start ||
212+ CrossAxisAlignmentC .stretch ||
213+ CrossAxisAlignmentC .baseline => WrapAlignment .start,
214+ };
215+
216+ /// [childWidget] as a child of a wrapping row/column.
217+ ///
218+ /// A [Wrap] has no free space to share, so children can't flex in it: a
219+ /// child that is expanded or flexible on the main axis, and a spacer, take
220+ /// their design main-axis size. Children keep their own size across the
221+ /// run (there is no stretch) and their own alignment is not applied.
222+ static Widget wrapChildInWrap (
223+ BaseNode node,
224+ RowColumnMixin parentRowColumn,
225+ Widget childWidget, {
226+ WidgetInserter ? flexibleSpaceBackground,
227+ }) {
228+ final bool isRow = parentRowColumn.rowColumnType == RowColumnType .row;
229+ final SizeFit mainFit = isRow ? node.horizontalFit : node.verticalFit;
230+ final double designMain = isRow
231+ ? node.outerBoxLocal.width
232+ : node.outerBoxLocal.height;
233+
234+ if (node is SpacerNode ) {
235+ return SizedBox (
236+ width: isRow ? designMain : 0 ,
237+ height: isRow ? 0 : designMain,
238+ );
239+ }
240+
241+ // A transformer may hand back a flex parent-data widget for a Flex.
242+ while (childWidget is Flexible ) {
243+ childWidget = childWidget.child;
244+ }
245+ if (flexibleSpaceBackground != null ) {
246+ childWidget = flexibleSpaceBackground (childWidget);
247+ }
248+ if (mainFit.isFlex) {
249+ childWidget = SizedBox (
250+ width: isRow ? designMain : null ,
251+ height: isRow ? null : designMain,
252+ child: childWidget,
253+ );
254+ }
255+ return childWidget;
256+ }
257+
149258 /// The design extent of a Row/Column's own box on one axis, or null when it
150259 /// hugs its children.
151260 ///
@@ -192,6 +301,15 @@ class PassiveRowColumnTransformer extends NodeWidgetTransformer<RowColumnNode> {
192301 }) {
193302 assert (parentRowColumn is RowColumnMixin );
194303
304+ if (rowColumnWraps (parentRowColumn)) {
305+ return wrapChildInWrap (
306+ node,
307+ parentRowColumn as RowColumnMixin ,
308+ childWidget,
309+ flexibleSpaceBackground: flexibleSpaceBackground,
310+ );
311+ }
312+
195313 if (node is SpacerNode ) {
196314 return childWidget;
197315 }
0 commit comments