Skip to content

Commit 6605f1b

Browse files
committed
Pass row/column spacing to the rendered Flex
The row/column transformer gives RowColumnMixin.spacing to its CodelesslyFlex, so gaps are set aside before flex children share the free space (three expanded children in a 1072 row with spacing 20 are 344 each). Scrollable and hugging rows/columns keep the gaps too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UErtKMSFkZWHUh8DrQMcZW
1 parent 8c68690 commit 6605f1b

2 files changed

Lines changed: 188 additions & 0 deletions

File tree

‎lib/src/transformers/node_transformers/passive_row_column_transformer.dart‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ class PassiveRowColumnTransformer extends NodeWidgetTransformer<RowColumnNode> {
9999
mainAxisSize: mainFit == SizeFit.shrinkWrap
100100
? MainAxisSize.min
101101
: MainAxisSize.max,
102+
spacing: rowColumnNode.spacing,
102103
key: ValueKey(rowColumnNode.id),
103104
children: childrenWidgets,
104105
);

‎test/row_column_spacing_test.dart‎

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import 'package:codelessly_api/codelessly_api.dart';
2+
import 'package:codelessly_sdk/codelessly_sdk.dart';
3+
import 'package:flutter/material.dart';
4+
import 'package:flutter_test/flutter_test.dart';
5+
import 'package:provider/provider.dart';
6+
7+
// RowColumnNode.spacing becomes the gap between children of the rendered
8+
// Row/Column, taken out of the main axis before flex children share the
9+
// free space.
10+
11+
RectangleNode rect(
12+
String id, {
13+
double width = 50,
14+
double height = 50,
15+
SizeFit horizontalFit = SizeFit.fixed,
16+
SizeFit verticalFit = SizeFit.fixed,
17+
}) => RectangleNode(
18+
id: id,
19+
name: id,
20+
basicBoxLocal: NodeBox(0, 0, width, height),
21+
horizontalFit: horizontalFit,
22+
verticalFit: verticalFit,
23+
);
24+
25+
RowColumnNode flex(
26+
String id,
27+
List<BaseNode> children, {
28+
required RowColumnType type,
29+
required double spacing,
30+
double width = 300,
31+
double height = 300,
32+
SizeFit horizontalFit = SizeFit.fixed,
33+
SizeFit verticalFit = SizeFit.fixed,
34+
bool isScrollable = false,
35+
}) {
36+
for (final child in children) {
37+
child.parentID = id;
38+
}
39+
return RowColumnNode(
40+
id: id,
41+
name: id,
42+
basicBoxLocal: NodeBox(0, 0, width, height),
43+
children: [for (final child in children) child.id],
44+
rowColumnType: type,
45+
horizontalFit: horizontalFit,
46+
verticalFit: verticalFit,
47+
isScrollable: isScrollable,
48+
mainAxisAlignment: MainAxisAlignmentC.start,
49+
crossAxisAlignment: CrossAxisAlignmentC.start,
50+
spacing: spacing,
51+
)..parentID = kRootNode;
52+
}
53+
54+
Finder rectOf(String id) =>
55+
find.byKey(ValueKey('Rectangle Transformer of $id [$id]'));
56+
57+
/// Pumps [root] at [screen] and returns each node's global rect.
58+
Future<Map<String, Rect>> pump(
59+
WidgetTester tester,
60+
BaseNode root,
61+
List<BaseNode> nodes, {
62+
Size screen = const Size(1440, 900),
63+
}) async {
64+
tester.view.physicalSize = screen;
65+
tester.view.devicePixelRatio = 1;
66+
addTearDown(tester.view.reset);
67+
68+
final registry = NodeRegistry()
69+
..setNodes({
70+
for (final node in [root, ...nodes]) node.id: node,
71+
});
72+
final manager = PassiveNodeTransformerManager(
73+
registry.getNodeByID,
74+
(context, bounds, pageID, layoutID, canvasID) => const SizedBox(),
75+
);
76+
await tester.pumpWidget(
77+
ChangeNotifierProvider<CodelesslyContext>.value(
78+
value: CodelesslyContext.empty(),
79+
child: MaterialApp(
80+
home: Material(
81+
child: Align(
82+
alignment: Alignment.topLeft,
83+
child: Builder(
84+
builder: (context) => manager.buildWidgetFromNode(
85+
root,
86+
context,
87+
settings: const WidgetBuildSettings(debugLabel: 'test'),
88+
),
89+
),
90+
),
91+
),
92+
),
93+
),
94+
);
95+
expect(tester.takeException(), isNull);
96+
return {
97+
for (final node in [root, ...nodes])
98+
if (rectOf(node.id).evaluate().isNotEmpty)
99+
node.id: tester.getRect(rectOf(node.id)),
100+
};
101+
}
102+
103+
void main() {
104+
testWidgets('expanded children share the room left after spacing', (
105+
tester,
106+
) async {
107+
final children = [
108+
for (final id in ['a', 'b', 'c'])
109+
rect(id, width: 100, horizontalFit: SizeFit.expanded),
110+
];
111+
final row = flex(
112+
'row',
113+
children,
114+
type: RowColumnType.row,
115+
spacing: 20,
116+
width: 1072,
117+
height: 50,
118+
);
119+
final rects = await pump(tester, row, children);
120+
121+
expect(rects['row']!.width, 1072);
122+
for (final id in ['a', 'b', 'c']) {
123+
expect(rects[id]!.width, 344, reason: id);
124+
}
125+
expect(rects['a']!.left, 0);
126+
expect(rects['b']!.left, 364);
127+
expect(rects['c']!.left, 728);
128+
});
129+
130+
testWidgets('a hugging column is as tall as its children plus gaps', (
131+
tester,
132+
) async {
133+
final children = [
134+
rect('a', height: 40),
135+
rect('b', height: 60),
136+
rect('c', height: 30),
137+
];
138+
final column = flex(
139+
'column',
140+
children,
141+
type: RowColumnType.column,
142+
spacing: 12,
143+
width: 50,
144+
height: 154,
145+
verticalFit: SizeFit.shrinkWrap,
146+
);
147+
final rects = await pump(tester, column, children);
148+
149+
expect(rects['column']!.height, 40 + 60 + 30 + 2 * 12);
150+
expect(rects['b']!.top, 52);
151+
expect(rects['c']!.top, 124);
152+
});
153+
154+
testWidgets('a scrollable row keeps its spacing', (tester) async {
155+
final children = [
156+
for (final id in ['a', 'b', 'c', 'd']) rect(id, width: 200),
157+
];
158+
final row = flex(
159+
'row',
160+
children,
161+
type: RowColumnType.row,
162+
spacing: 16,
163+
width: 400,
164+
height: 50,
165+
isScrollable: true,
166+
);
167+
final rects = await pump(tester, row, children);
168+
169+
expect(rects['b']!.left - rects['a']!.right, 16);
170+
expect(rects['c']!.left - rects['b']!.right, 16);
171+
});
172+
173+
testWidgets('zero spacing leaves children adjacent', (tester) async {
174+
final children = [rect('a'), rect('b')];
175+
final row = flex(
176+
'row',
177+
children,
178+
type: RowColumnType.row,
179+
spacing: 0,
180+
width: 300,
181+
height: 50,
182+
);
183+
final rects = await pump(tester, row, children);
184+
185+
expect(rects['b']!.left, rects['a']!.right);
186+
});
187+
}

0 commit comments

Comments
 (0)