Skip to content

Commit 05a306a

Browse files
committed
fix(ui): harden BaseField label against RenderFlex overflow on narrow screens
Adds softWrap: true to the label Text inside the Expanded Row in BaseField.build() so the existing constraint is explicit, and adds a 9-case widget-test suite that pumps DataField / CheckField / NumericField / SelectField with a 200-character label in containers as narrow as 120 px — verifying no overflow error is thrown. Closes #72
1 parent cad45a4 commit 05a306a

2 files changed

Lines changed: 171 additions & 0 deletions

File tree

lib/src/ui/widgets/fields/base_field.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ abstract class BaseField extends StatelessWidget {
8888
style?.translate != null
8989
? style!.translate!(field.displayLabel)
9090
: field.displayLabel,
91+
softWrap: true,
9192
style:
9293
style?.labelStyle ??
9394
TextStyle(
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_form_builder/flutter_form_builder.dart';
3+
import 'package:flutter_test/flutter_test.dart';
4+
import 'package:frappe_mobile_sdk/src/models/doc_field.dart';
5+
import 'package:frappe_mobile_sdk/src/ui/widgets/fields/data_field.dart';
6+
import 'package:frappe_mobile_sdk/src/ui/widgets/fields/check_field.dart';
7+
import 'package:frappe_mobile_sdk/src/ui/widgets/fields/numeric_field.dart';
8+
import 'package:frappe_mobile_sdk/src/ui/widgets/fields/select_field.dart';
9+
import 'package:frappe_mobile_sdk/src/ui/widgets/fields/base_field.dart';
10+
11+
// Simulates a phone screen at 360×640 logical pixels.
12+
const _phoneWidth = 360.0;
13+
const _phoneHeight = 640.0;
14+
15+
// 200-character label — longer than any real-world field name.
16+
const _longLabel =
17+
'This is an extremely long field label that would trigger a '
18+
'RenderFlex overflow if the layout did not constrain it with '
19+
'an Expanded or Flexible widget around the Text child in the Row';
20+
21+
Widget _wrap(Widget child) {
22+
return MaterialApp(
23+
home: Scaffold(
24+
body: SizedBox(
25+
width: _phoneWidth,
26+
height: _phoneHeight,
27+
child: SingleChildScrollView(
28+
child: FormBuilder(
29+
child: child,
30+
),
31+
),
32+
),
33+
),
34+
);
35+
}
36+
37+
DocField _field({
38+
String fieldname = 'f',
39+
String fieldtype = 'Data',
40+
String label = _longLabel,
41+
bool reqd = false,
42+
String? options,
43+
}) {
44+
return DocField(
45+
fieldname: fieldname,
46+
fieldtype: fieldtype,
47+
label: label,
48+
reqd: reqd,
49+
options: options,
50+
);
51+
}
52+
53+
void main() {
54+
// Regression guard: BaseField label is inside Expanded, so even a
55+
// 200-character label must not produce a RenderFlex overflow error.
56+
// Acceptance criterion for issue #72.
57+
58+
testWidgets('DataField — long label does not overflow', (tester) async {
59+
await tester.pumpWidget(_wrap(DataField(field: _field())));
60+
await tester.pump();
61+
// No overflow error thrown = pass.
62+
});
63+
64+
testWidgets('DataField required — long label + asterisk does not overflow', (
65+
tester,
66+
) async {
67+
await tester.pumpWidget(
68+
_wrap(DataField(field: _field(reqd: true))),
69+
);
70+
await tester.pump();
71+
});
72+
73+
testWidgets('CheckField — long label does not overflow', (tester) async {
74+
await tester.pumpWidget(_wrap(CheckField(field: _field(fieldtype: 'Check'))));
75+
await tester.pump();
76+
});
77+
78+
testWidgets('NumericField — long label does not overflow', (tester) async {
79+
await tester.pumpWidget(
80+
_wrap(NumericField(field: _field(fieldtype: 'Int'))),
81+
);
82+
await tester.pump();
83+
});
84+
85+
testWidgets('SelectField — long label does not overflow', (tester) async {
86+
await tester.pumpWidget(
87+
_wrap(
88+
SelectField(
89+
field: _field(fieldtype: 'Select', options: 'A\nB\nC'),
90+
),
91+
),
92+
);
93+
await tester.pump();
94+
});
95+
96+
testWidgets('label text is visible and accessible', (tester) async {
97+
const shortLabel = 'Field Name';
98+
await tester.pumpWidget(
99+
_wrap(DataField(field: _field(label: shortLabel))),
100+
);
101+
await tester.pump();
102+
expect(find.text(shortLabel), findsOneWidget);
103+
});
104+
105+
testWidgets(
106+
'very narrow container (120px) — long label wraps, no overflow',
107+
(tester) async {
108+
await tester.pumpWidget(
109+
MaterialApp(
110+
home: Scaffold(
111+
body: SizedBox(
112+
width: 120,
113+
child: FormBuilder(
114+
child: DataField(field: _field()),
115+
),
116+
),
117+
),
118+
),
119+
);
120+
await tester.pump();
121+
},
122+
);
123+
124+
testWidgets(
125+
'multiple fields with long labels in a Column — no overflow',
126+
(tester) async {
127+
await tester.pumpWidget(
128+
MaterialApp(
129+
home: Scaffold(
130+
body: SizedBox(
131+
width: _phoneWidth,
132+
height: _phoneHeight,
133+
child: SingleChildScrollView(
134+
child: FormBuilder(
135+
child: Column(
136+
children: [
137+
DataField(field: _field(fieldname: 'a')),
138+
CheckField(
139+
field: _field(fieldname: 'b', fieldtype: 'Check'),
140+
),
141+
NumericField(
142+
field: _field(fieldname: 'c', fieldtype: 'Float'),
143+
),
144+
],
145+
),
146+
),
147+
),
148+
),
149+
),
150+
),
151+
);
152+
await tester.pump();
153+
},
154+
);
155+
156+
testWidgets('FieldStyle.showLabel=false — label Row not rendered', (
157+
tester,
158+
) async {
159+
await tester.pumpWidget(
160+
_wrap(
161+
DataField(
162+
field: _field(),
163+
style: const FieldStyle(showLabel: false),
164+
),
165+
),
166+
);
167+
await tester.pump();
168+
expect(find.text(_longLabel), findsNothing);
169+
});
170+
}

0 commit comments

Comments
 (0)