Text input is one of the most important aspects of modern applications. Whether it is entering a username, password, email address, or submitting a message in a chat application, handling text input is essential. In Flutter, the widget responsible for this functionality is the TextField widget.
The TextField widget is powerful, flexible, and customizable. It allows developers to capture and display user input, style the field with decorations, handle input changes dynamically, and control its content using controllers.
In this article, we will explore the TextField widget in depth by covering:
- Single-line vs multi-line input
- Decorations such as hintText, labelText, and border
- Handling input with onChanged
- Using controllers to manage input
- Best practices for TextField usage
- Common mistakes and optimization tips
By the end, you will have a strong understanding of how to implement and customize text input fields in Flutter applications.
Introduction to the TextField Widget
In Flutter, a TextField is a widget that allows users to enter text through the device’s keyboard. It is part of the material.dart library and follows Material Design guidelines, although it can be styled to match other design systems.
Basic syntax:
TextField(
decoration: InputDecoration(
hintText: 'Enter your name',
),
)
This simple example creates a single-line text input with a placeholder hint. However, the TextField widget offers much more than that.
Single-Line vs Multi-Line Input
By default, a TextField is designed for single-line input. This is useful for fields like email, username, password, or search queries. However, there are times when you need to capture more extensive input, such as writing a message or entering a description.
Single-Line Input
Single-line is the default mode.
Example:
TextField(
decoration: InputDecoration(
labelText: 'Username',
),
)
- The keyboard closes automatically when the user presses enter.
- Suitable for short input like names, emails, or numbers.
Multi-Line Input
To allow multi-line input, you can use the maxLines property.
Example:
TextField(
maxLines: 5,
decoration: InputDecoration(
labelText: 'Description',
),
)
- Here, the input expands up to 5 lines.
- Setting
maxLines: nullmakes the TextField grow dynamically as the user types.
This feature is ideal for comments, messages, or large forms.
Input Decorations
One of the most powerful features of the TextField widget is InputDecoration. With InputDecoration, you can customize the appearance of your TextField to improve usability and match your design requirements.
hintText
- Appears when the field is empty.
- Disappears once the user types something.
Example:
TextField(
decoration: InputDecoration(
hintText: 'Enter your email',
),
)
Use case: Helpful for giving context when a label is not provided.
labelText
- Always visible as a floating label above the field when focused.
- Improves accessibility and clarity.
Example:
TextField(
decoration: InputDecoration(
labelText: 'Password',
),
)
Use case: Preferred for forms where the label must be persistent.
border
Borders define how the text field is visually separated. Flutter provides multiple border styles.
Example with Outline border:
TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Full Name',
),
)
Types of borders available:
UnderlineInputBorder()– Default material underline.OutlineInputBorder()– Full rectangle border.InputBorder.none– Removes border completely.
Combining Decorations
You can mix hintText, labelText, and borders for a polished look.
Example:
TextField(
decoration: InputDecoration(
hintText: 'Enter your message',
labelText: 'Message',
border: OutlineInputBorder(),
),
)
This makes the field clear, user-friendly, and professional.
Handling onChanged
The onChanged callback allows you to respond immediately when the text changes. It is triggered every time the user types, deletes, or modifies the input.
Example:
TextField(
onChanged: (value) {
print('Current input: $value');
},
decoration: InputDecoration(
labelText: 'Search',
),
)
Use cases:
- Real-time form validation.
- Implementing live search.
- Enabling or disabling buttons depending on user input.
Using Controllers
A TextEditingController gives you direct control over the text inside a TextField. Unlike onChanged, which responds reactively, controllers allow you to programmatically set, clear, or read the input.
Declaring a Controller
final TextEditingController _controller = TextEditingController();
Assigning Controller to TextField
TextField(
controller: _controller,
decoration: InputDecoration(
labelText: 'Enter text',
),
)
Reading Input
print(_controller.text);
Setting Input
_controller.text = "Default value";
Clearing Input
_controller.clear();
Adding Listeners
_controller.addListener(() {
print("Current value: ${_controller.text}");
});
Controllers are especially powerful in form-heavy applications where you need explicit control over the data entered.
Practical Example: Login Form
Here is a basic login form that demonstrates single-line input, decoration, onChanged, and controllers.
class LoginForm extends StatefulWidget {
@override
_LoginFormState createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Login')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _emailController,
decoration: InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
onChanged: (value) {
print('Email changed: $value');
},
),
SizedBox(height: 16),
TextField(
controller: _passwordController,
obscureText: true,
decoration: InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
onChanged: (value) {
print('Password changed: $value');
},
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
print('Email: ${_emailController.text}');
print('Password: ${_passwordController.text}');
},
child: Text('Login'),
),
],
),
),
);
}
}
This example:
- Uses controllers to access entered values.
- Prints changes in real time with onChanged.
- Styles fields with borders and labels.
Best Practices with TextField
- Use controllers for important input
- Provides more control compared to relying solely on onChanged.
- Validate input
- Use form validation for fields like email, phone numbers, and passwords.
- Use appropriate input types
- For numeric input:
keyboardType: TextInputType.number. - For emails:
keyboardType: TextInputType.emailAddress.
- For numeric input:
- Handle focus management
- Use
FocusNodeto switch between fields automatically.
- Use
- Secure sensitive input
- Use
obscureText: truefor passwords.
- Use
- Accessibility
- Always include labels to make fields accessible to screen readers.
Common Mistakes with TextField
- Forgetting to dispose controllers
- Always call
_controller.dispose()indispose()method to prevent memory leaks.
- Always call
- Overusing onChanged for heavy tasks
- Avoid expensive operations inside onChanged because it triggers on every keystroke.
- Not setting keyboard type
- Makes the typing experience harder for users (for example, asking for a number but showing an alphabetic keyboard).
- Ignoring input validation
- Leads to errors in forms when input is not properly checked.
Leave a Reply