Skip to content

Commit 6f9d063

Browse files
fix(bull_ui): keep numeric inputs single-line
1 parent 037237a commit 6f9d063

2 files changed

Lines changed: 82 additions & 2 deletions

File tree

packages/bull_ui/lib/src/inputs/bull_input_text.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,12 @@ class _BullInputTextState extends State<BullInputText> {
143143
@override
144144
Widget build(BuildContext context) {
145145
final colors = context.bull;
146+
// Numeric and obscured fields are never legitimately multiline, so they
147+
// default to a single line instead of Flutter's unlimited `null`.
148+
final effectiveMaxLines =
149+
widget.maxLines ?? (widget.obscure || widget.onlyNumbers ? 1 : null);
146150
final shouldPreventNewlines =
147-
widget.maxLines != null && widget.maxLines! <= 2;
151+
effectiveMaxLines != null && effectiveMaxLines <= 2;
148152

149153
return TextField(
150154
key: widget.uiKey,
@@ -171,7 +175,7 @@ class _BullInputTextState extends State<BullInputText> {
171175
enableIMEPersonalizedLearning: false,
172176
maxLength: widget.maxLength,
173177
minLines: widget.minLines ?? 1,
174-
maxLines: widget.maxLines ?? (widget.obscure ? 1 : null),
178+
maxLines: effectiveMaxLines,
175179
style:
176180
widget.style ??
177181
Theme.of(context).textTheme.headlineSmall?.copyWith(
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import 'package:bull_ui/bull_ui.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:flutter_test/flutter_test.dart';
4+
5+
import 'test_app.dart';
6+
7+
void main() {
8+
Future<void> pumpInput(
9+
WidgetTester tester, {
10+
bool onlyNumbers = false,
11+
bool obscure = false,
12+
int? maxLines,
13+
}) async {
14+
await tester.pumpWidget(
15+
wrapWithTheme(
16+
BullInputText(
17+
value: '',
18+
onlyNumbers: onlyNumbers,
19+
obscure: obscure,
20+
maxLines: maxLines,
21+
onChanged: (_) {},
22+
),
23+
),
24+
);
25+
}
26+
27+
TextField findTextField(WidgetTester tester) =>
28+
tester.widget<TextField>(find.byType(TextField));
29+
30+
group('BullInputText line behaviour', () {
31+
testWidgets('numeric fields are single-line', (tester) async {
32+
await pumpInput(tester, onlyNumbers: true);
33+
34+
expect(findTextField(tester).maxLines, 1);
35+
});
36+
37+
testWidgets('numeric fields submit instead of inserting a newline', (
38+
tester,
39+
) async {
40+
await pumpInput(tester, onlyNumbers: true);
41+
42+
expect(findTextField(tester).textInputAction, TextInputAction.done);
43+
});
44+
45+
testWidgets('numeric fields reject newline characters', (tester) async {
46+
await pumpInput(tester, onlyNumbers: true);
47+
48+
await tester.enterText(find.byType(TextField), '10\n\n20');
49+
await tester.pump();
50+
51+
expect(find.text('1020'), findsOneWidget);
52+
});
53+
54+
testWidgets('obscured fields stay single-line', (tester) async {
55+
await pumpInput(tester, obscure: true);
56+
57+
expect(findTextField(tester).maxLines, 1);
58+
});
59+
60+
testWidgets('free-text fields remain multiline', (tester) async {
61+
await pumpInput(tester);
62+
63+
final textField = findTextField(tester);
64+
expect(textField.maxLines, isNull);
65+
expect(textField.textInputAction, TextInputAction.newline);
66+
});
67+
68+
testWidgets('an explicit maxLines wins over the numeric default', (
69+
tester,
70+
) async {
71+
await pumpInput(tester, onlyNumbers: true, maxLines: 3);
72+
73+
expect(findTextField(tester).maxLines, 3);
74+
});
75+
});
76+
}

0 commit comments

Comments
 (0)