TextField en Flutter
Domina la captura de entrada de usuario en tus aplicaciones
Descargar Ejemplos Completos¿Qué es TextField?
El TextField es uno de los widgets más importantes en Flutter para capturar entrada de texto del usuario. Es esencial para formularios, búsquedas, login y cualquier interacción que requiera texto.
Características Principales:
- ✅ Captura de texto simple y seguro
- ✅ Validación y manejo de errores
- ✅ Diferentes tipos de teclado
- ✅ Decoración y personalización completa
- ✅ Integración con Form y FormField
Sintaxis Básica
TextField Mínimo
TextField(
onChanged: (text) {
print('Texto cambiado: $text');
},
)
TextField con Controlador
final TextEditingController _controller = TextEditingController();
TextField(
controller: _controller,
decoration: InputDecoration(
labelText: 'Nombre',
hintText: 'Escribe tu nombre',
border: OutlineInputBorder(),
),
)
Propiedades Principales
controller
Controla el texto y estado del TextField
Tipo: TextEditingController
Requerido: No
decoration
Personaliza la apariencia del TextField
Tipo: InputDecoration
Requerido: No
onChanged
Función llamada cuando el texto cambia
Tipo: ValueChanged<String>
Requerido: No
keyboardType
Tipo de teclado a mostrar
Tipo: TextInputType
Por defecto: TextInputType.text
obscureText
Oculta el texto (para contraseñas)
Tipo: bool
Por defecto: false
maxLines
Número máximo de líneas
Tipo: int
Por defecto: 1
InputDecoration
La propiedad decoration permite personalizar completamente la apariencia del TextField.
labelText
Texto que aparece como etiqueta
hintText
Texto de sugerencia cuando está vacío
prefixIcon / suffixIcon
Iconos antes/después del texto
errorText
Texto de error cuando hay validación
Ejemplo Completo de Decoración:
TextField(
decoration: InputDecoration(
labelText: 'Correo electrónico',
hintText: 'tu@email.com',
prefixIcon: Icon(Icons.email),
suffixIcon: Icon(Icons.check_circle, color: Colors.green),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 2),
),
errorText: _emailError,
errorStyle: TextStyle(color: Colors.red),
),
)
Tipos de TextField
1. TextField Básico
TextField()
2. Con Borde Outline
border: OutlineInputBorder()
3. Con Icono
prefixIcon: Icon(Icons.search)
4. Para Contraseña
obscureText: true
5. Multilínea
maxLines: 5
6. Con Validación
errorText: 'Campo requerido'
Manejo de Estado
1. TextEditingController
Para control avanzado del texto y estado
class _MyFormState extends State<MyForm> {
final TextEditingController _controller = TextEditingController();
@override
void initState() {
super.initState();
_controller.addListener(_onTextChanged);
}
void _onTextChanged() {
print('Texto actual: ${_controller.text}');
}
@override
Widget build(BuildContext context) {
return TextField(
controller: _controller,
decoration: InputDecoration(labelText: 'Nombre'),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
2. onChanged con setState
Para casos simples sin necesidad de controlador
class _MyFormState extends State<MyForm> {
String _text = '';
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
onChanged: (text) {
setState(() {
_text = text;
});
},
decoration: InputDecoration(labelText: 'Escribe algo'),
),
Text('Has escrito: $_text'),
],
);
}
}
Tipos de Teclado
TextInputType.text
Teclado estándar para texto
keyboardType: TextInputType.text
TextInputType.emailAddress
Teclado optimizado para emails
keyboardType: TextInputType.emailAddress
TextInputType.phone
Teclado numérico para teléfonos
keyboardType: TextInputType.phone
TextInputType.number
Teclado numérico estándar
keyboardType: TextInputType.number
TextInputType.multiline
Optimizado para texto multilínea
keyboardType: TextInputType.multiline
Personalizador de TextField
// El código se generará aquí
Validación de Formularios
Validación con Form
Usando Form y TextFormField para validación integrada
final _formKey = GlobalKey<FormState>();
Form(
key: _formKey,
child: Column(
children: [
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Por favor ingresa tu nombre';
}
return null;
},
decoration: InputDecoration(labelText: 'Nombre'),
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// Formulario válido
}
},
child: Text('Enviar'),
),
],
),
)
Validación Personalizada
Validaciones específicas para diferentes casos
String? _validateEmail(String? value) {
if (value == null || value.isEmpty) {
return 'El email es requerido';
}
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
return 'Ingresa un email válido';
}
return null;
}
TextFormField(
validator: _validateEmail,
decoration: InputDecoration(labelText: 'Email'),
)
Mejores Prácticas
✅ Lo que SÍ debes hacer
- Usa TextEditingController para campos importantes
- Implementa validación en todos los campos de formulario
- Usa el keyboardType apropiado para cada campo
- Proporciona hintText y labelText claros
- Maneja el dispose de los controllers
❌ Lo que NO debes hacer
- No olvides limpiar los controllers
- No uses onChanged para validación en tiempo real compleja
- No ignores la accesibilidad (semanticLabel)
- No uses obscureText para campos que no son contraseñas
- No olvides el manejo de errores
Tip Profesional
Para formularios complejos, considera usar packages como flutter_form_builder o reactive_forms que proporcionan validación más avanzada y manejo de estado.
Ejercicios para Practicar
Ejercicio 1: Formulario de Registro
Crea un formulario de registro con validación para nombre, email y contraseña.
Campos: Nombre (requerido), Email (validación), Contraseña (mínimo 6 caracteres)
Ejercicio 2: Búsqueda en Tiempo Real
Implementa un campo de búsqueda que filtre una lista mientras el usuario escribe.
Pista: Usa onChanged con debounce para optimizar
Ejercicio 3: Formulario de Contacto
Crea un formulario de contacto con campos para nombre, email, asunto y mensaje multilínea.
Campos: Usa TextFormField con validación personalizada
Solución de Problemas Comunes
❌ El texto no se actualiza
Solución: Asegúrate de llamar setState() o usar un controller
setState(() { _text = newValue; });
❌ Error con el controller
Solución: No olvides llamar dispose() en el controller
@override
void dispose() {
_controller.dispose();
super.dispose();
}
❌ El teclado no muestra @ para emails
Solución: Usa TextInputType.emailAddress
keyboardType: TextInputType.emailAddress