diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart index 952e55ecc2..1a50ab05eb 100644 --- a/packages/flutter/lib/src/material/text_field.dart +++ b/packages/flutter/lib/src/material/text_field.dart @@ -78,7 +78,10 @@ class TextField extends StatefulWidget { /// 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 @@ class TextField extends StatefulWidget { /// {@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 @@ class _TextFieldState extends State with AutomaticKeepAliveClientMixi 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 0afdd51bd1..945a887ff4 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -1790,16 +1790,14 @@ void main() { 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 @@ void main() { 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.isTextField])); @@ -3162,9 +3179,7 @@ void main() { 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 @@ void main() { ), ), ), - ), ); await tester.tap(find.byType(TextField));