Most modern applications require some type of input from a user. Whether it be a signup, login, or feedback form, learning how to implement a text field is an important skill to master as a developer.
In Flutter, there are two types of text field widgets that we can use to get user input. One is TextField
and the other one is TextFormField
, a slightly more advanced version of TextField
. TextFormField
provides more functionalities than TextField
, such as build form validation and the ability to set initial text value directly.
If your text field requires only one or two inputs from the user, I suggest using the TextField
widget. Otherwise if you want to create a larger form with multiple input fields and validation, it’s better to go with the TextFormField
widget.
TextField
Creating a basic TextField
widget is straightforward. Apply the TextField
widget inside your widget tree where you want it to appear. This will add a default TextField
with default styling:
TextField( )
TextFormField
You can add TextFormField
in the same manner as TextField
. There is no visual difference between these two widgets:
TextFormField( )
Styling a text field to personalize your application is easily done by setting InputDecoration
to the decoration
property of the TextField
/TextFormField
widget:
TextField( decoration: InputDecoration( filled: true, fillColor: Colors.blueAccent, border: OutlineInputBorder( borderSide: BorderSide.none, borderRadius: BorderRadius.circular(50) ), ), )
You must set the filled
value to true
if you want to apply a background color to your text field. Otherwise, the background color will not be affected.
Text color can be changed using the style
property of the TextField
widget. You can also change the cursor color by setting the color to the cursorColor
property:
TextField( cursorColor: Colors.black, style: TextStyle( color: Colors.white ), decoration: InputDecoration( filled: true, fillColor: Colors.blueAccent, border: OutlineInputBorder( borderSide: BorderSide.none, borderRadius: BorderRadius.circular(50) ), ), )
Hint text is used to give users an idea about the input values that are accepted by the text field. You can use the hintText
property to add a hint to the text field which will disappear when you begin typing. The default color is grey, but you can add hintStyle
to change the text styling:
TextField( decoration: InputDecoration( hintStyle: TextStyle(color: Colors.blue), hintText: "Enter your name" ), )
By default, TextField
shows as a single line. But we can specify the maximum number of lines to be supported via the maxLines
property. This will not limit the number of lines you can add, it only shows the specified number of lines at a time. If you want to expand the field based on the amount of input text, you can set null
to the maxLines
property:
TextField( maxLines: 5, ),
Reading the user’s input is the most important feature of your text field. In Flutter, this can be done using TextEditingController
.
First, create a TextEditingController
and set it as a controller property of your TextField
widget.
In this example, I have added an extra Button
and Text
widget which will show the added text when you click the “Show Text” button.
When you press the button, it will set the textController
value to the displayText
variable. displayText
has been set as the text of the Text
widget, so when you press “Show Text” you can see the input text appear:
class _TextFormState extends State<TextFormSample> { TextEditingController textController = TextEditingController(); String displayText = ""; @override Widget build(BuildContext context) { return Column( children: [ TextField( controller: textController, maxLines: null, ), ElevatedButton(onPressed: (){ setState(() { displayText = textController.text; }); }, child: Text("Show Text")), Text(displayText,style: TextStyle(fontSize: 20),) ], ); } }
Pre-populating values when loading the text field will be useful in scenarios like profile updates and login screens. The TextField
widget itself doesn’t have a property to set an initial value, but this can be done using TextEditingController
.
Create a TextEditingController
, set a value to the text
property of the constructor, and it will populate to the widget when it loads the first time:
TextEditingController textController = TextEditingController(text: "Initial Text"); @override Widget build(BuildContext context) { return Center( child: TextField( controller: textController, ), ); }
Then use the initialValue
property in TextFormField
to create your pre-populated text:
TextFormField( initialValue: "Initial Text", )
You may have seen applications show different keyboard layouts for different input types, like number pads for phone numbers or an “@” button for emails. This can be done in Flutter via the keyboardType
property. It accepts TextInputType
with multiple options like number, date, phone, name, and email address:
TextField( keyboardType: TextInputType.number, )
By setting the obscureText
property to true
you can convert a plain text field to a password field, which masks the input values.
The default of this property will show dots to mask password characters. But you can change this by setting the obscuringCharacter
value to anything you’d like; here, I chose asterisks:
TextField( obscureText: true, obscuringCharacter: "*", )
The maxLength
property accepts integer values to specify the maximum number of characters accepted by the particular field. After adding this property, if users enter a value with more characters than specified in maxLength
, it will block the input automatically:
TextField( maxLength: 2, )
Utilizing validation in your text field to restrict certain characters or digits is a must to reduce user errors.
Flutter’s inputFormatter
property allows you to set an array of filters to the TextField
widget. It will accept two types:
FilteringTextInputFormatter.allow()
FilteringTextInputFormatter.deny()
The following is an example of what your code might look like if you’re denying certain characters:
TextField( inputFormatters: [FilteringTextInputFormatter.deny(RegExp("[0-9]+"))], )
If a user enters a denied character, the text field will not display an error to the user. It simply blocks or allows specified characters based on the input.
However, adding error messages with validation is simple, which is what we are going to talk about next.
Applying an error message to TextField
and TextFormField
is slightly different because of the availability of certain properties. Let’s take a look at how you can validate input with error messages in each of these widgets.
TextField
There is no direct property to add an error message in TextField
. But you can set an errorText
property in InputDecoration
based on the validated value.
In the following example, I determine if the input value is empty and a number, and change the isANumber
value to true or false based on the result. Based on the isANumber
value you can set the error text, as I did here with “Please enter a number”:
class _LoginFormState extends State<LoginForm> { TextEditingController textController = TextEditingController(); RegExp digitValidator = RegExp("[0-9]+"); bool isANumber = true; @override Widget build(BuildContext context) { return Center( child: TextField( onChanged: (inputValue){ if(inputValue.isEmpty || digitValidator.hasMatch(inputValue)){ setValidator(true); } else{ setValidator(false); } }, decoration: InputDecoration( errorText: isANumber ? null : "Please enter a number" ), ), ); } void setValidator(valid){ setState(() { isANumber = valid; }); } }
You can easily customize error text color by setting TextStyle
to the errorStyle
property.
You can change the border color using the focusedErrorBorder
and errorBorder
properties. errorBorder
will be shown when there is no focus on the field. Therefore, be sure to set both of those properties when changing the border color:
TextField( onChanged: (inputValue){ if(inputValue.isEmpty || digitValidator.hasMatch(inputValue)){ setValidator(true); } else{ setValidator(false); } }, decoration: InputDecoration( errorText: isANumber ? null : "Please enter a number", errorStyle: TextStyle(color: Colors.purpleAccent), focusedErrorBorder: UnderlineInputBorder(borderSide: BorderSide(color: Colors.purpleAccent)), errorBorder: UnderlineInputBorder(borderSide: BorderSide(color: Colors.purpleAccent)) ), )
TextFormField
The main difference between TextFormField
and TextField
is that the TextFormField
widget uses the Form
widget, which can contain multiple TextField
widgets.
In Flutter, creating a validated Form
with TextFormField
is simple.
First, create a Form
widget and add two TextFormField
widgets with a button (I used ElevatedButton
) and Text
.
The important thing to remember when creating a Form
widget is that you must first create a GlobalKey
which is required to access the Form
. After creating a GlobalKey
, you can set that key to the key
property of the Form
widget.
TextFormField
contains a property called a validator. You can access field values in the validator callback function and validate differently based on the returned value. For the first text field, we will check whether it is empty, or whether the value is a digit using a regular expression. If that condition fails you can return an error message for that particular field.
In the onPressed
event, you can check the form validity using the GlobalKey
object and change the isValidForm
value to true
or false
to show a message in the below Text
widget:
class _NumberFormState extends State<NumberForm> { var _numberForm = GlobalKey<FormState>(); RegExp _digitRegex = RegExp("[0-9]+"); bool isValidForm = false; @override Widget build(BuildContext context) { return Center( child: Form( key: _numberForm, child: Column( children: [ TextFormField( validator: (inputValue){ if(inputValue.isEmpty || !_digitRegex.hasMatch(inputValue)){ return "Please enter number"; } return null; }, ), TextFormField( validator: (inputValue){ if(inputValue.isEmpty){ return "Please Fill before"; } return null; }, ), ElevatedButton( onPressed: (){ if(_numberForm.currentState.validate()){ setState(() { isValidForm = true; }); } else{ setState(() { isValidForm = false; }); } }, child: Text("Check Number")), Text( isValidForm ? "Nice" : "Please Fix error and Submit ") ], )), ); } }
I hope this article gave you a better idea about how to customize and use different properties in Flutter’s TextField
and TextFormField
widgets.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowJavaScript’s Date API has many limitations. Explore alternative libraries like Moment.js, date-fns, and the new Temporal API.
Explore use cases for using npm vs. npx such as long-term dependency management or temporary tasks and running packages on the fly.
Validating and auditing AI-generated code reduces code errors and ensures that code is compliant.
Build a real-time image background remover in Vue using Transformers.js and WebGPU for client-side processing with privacy and efficiency.