Optionally show the text field length but not the max (#24183)
If maxLength is set to TextField.noMaxLength then continue to show the character counter but do not display the max input length value.
diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart
index 952e55e..1a50ab0 100644
--- a/packages/flutter/lib/src/material/text_field.dart
+++ b/packages/flutter/lib/src/material/text_field.dart
@@ -78,7 +78,10 @@
/// number of characters allowed in the text field is not restricted. If
/// [maxLength] is set, a character counter will be displayed below the
/// field, showing how many characters have been entered and how many are
- /// allowed. After [maxLength] characters have been input, additional input
+ /// allowed unless the value is set to [noMaxLength] in which case only the
+ /// current length is displayed.
+ ///
+ /// After [maxLength] characters have been input, additional input
/// is ignored, unless [maxLengthEnforced] is set to false. The TextField
/// enforces the length with a [LengthLimitingTextInputFormatter], which is
/// evaluated after the supplied [inputFormatters], if any. The [maxLength]
@@ -192,6 +195,10 @@
/// {@macro flutter.widgets.editableText.maxLines}
final int maxLines;
+ /// If [maxLength] is set to this value, only the "current input length"
+ /// part of the character counter is shown.
+ static const int noMaxLength = 9007199254740992; // math.pow(2, 53);
+
/// The maximum number of characters (Unicode scalar values) to allow in the
/// text field.
///
@@ -339,9 +346,16 @@
return effectiveDecoration;
final int currentLength = _effectiveController.value.text.runes.length;
- final String counterText = '$currentLength/${widget.maxLength}';
- final int remaining = (widget.maxLength - currentLength).clamp(0, widget.maxLength);
- final String semanticCounterText = localizations.remainingTextFieldCharacterCount(remaining);
+ String counterText = '$currentLength';
+ String semanticCounterText = '';
+
+ if (widget.maxLength != TextField.noMaxLength) {
+ counterText += '/${widget.maxLength}';
+ final int remaining = (widget.maxLength - currentLength).clamp(0, widget.maxLength);
+ semanticCounterText = localizations.remainingTextFieldCharacterCount(remaining);
+ }
+
+ // Handle length exceeds maxLength
if (_effectiveController.value.text.runes.length > widget.maxLength) {
final ThemeData themeData = Theme.of(context);
return effectiveDecoration.copyWith(
diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart
index 0afdd51..945a887 100644
--- a/packages/flutter/test/material/text_field_test.dart
+++ b/packages/flutter/test/material/text_field_test.dart
@@ -1790,16 +1790,14 @@
testWidgets('setting maxLength shows counter', (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(
home: Material(
- child: DefaultTextStyle(
- style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
- child: Center(
+ child: Center(
child: TextField(
maxLength: 10,
),
),
),
),
- ));
+ );
expect(find.text('0/10'), findsOneWidget);
@@ -1809,22 +1807,41 @@
expect(find.text('5/10'), findsOneWidget);
});
+ testWidgets(
+ 'setting maxLength to TextField.noMaxLength shows only entered length',
+ (WidgetTester tester) async {
+ await tester.pumpWidget(const MaterialApp(
+ home: Material(
+ child: Center(
+ child: TextField(
+ maxLength: TextField.noMaxLength,
+ ),
+ ),
+ ),
+ ),
+ );
+
+ expect(find.text('0'), findsOneWidget);
+
+ await tester.enterText(find.byType(TextField), '01234');
+ await tester.pump();
+
+ expect(find.text('5'), findsOneWidget);
+ });
+
testWidgets('TextField identifies as text field in semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(
const MaterialApp(
home: Material(
- child: DefaultTextStyle(
- style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
- child: Center(
+ child: Center(
child: TextField(
maxLength: 10,
),
),
),
),
- ),
);
expect(semantics, includesNodeWith(flags: <SemanticsFlag>[SemanticsFlag.isTextField]));
@@ -3162,9 +3179,7 @@
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
- body: DefaultTextStyle(
- style: const TextStyle(fontSize: 12.0, fontFamily: 'Ahem'),
- child: MediaQuery(
+ body: MediaQuery(
data: MediaQueryData.fromWindow(ui.window).copyWith(textScaleFactor: 4.0),
child: Center(
child: TextField(
@@ -3175,7 +3190,6 @@
),
),
),
- ),
);
await tester.tap(find.byType(TextField));