Un input de texto es un widget que permite al usuario introducir y editar texto.
Ya sabemos tocar la pantalla, ahora vamos a aprender a escribir en ella.
El teclado virtual es la principal vía de entrada de datos en un móvil. Ya sea para un login, un buscador o un chat, necesitamos dominar el widget TextField.
A diferencia de un simple botón, un campo de texto tiene mucha complejidad interna: gestión del teclado, posición del cursor, selección de texto, ocultación de contraseñas… Por suerte, Flutter nos lo da (casi) todo hecho.
El widget TextField
En su forma más básica, es un componente que al tocarlo despliega el teclado del sistema.
TextField() // Así de simple, ya funcionaPero claro, esto es solo una línea subrayada. Para que sea usable, necesitamos decorarlo y, lo más importante, leer lo que el usuario escribe.
Capturar el texto con TextEditingController
Hay dos formas de leer el texto: el callback onChanged y el controlador.
onChanged: Te avisa cada vez que el usuario pulsa una tecla. Es útil para buscadores en tiempo real.TextEditingController: permite leer el texto cuando queramos (por ejemplo, al pulsar Guardar) y también modificarlo desde código.
Implementar el controlador
El controlador es un objeto que debe vivir durante todo el ciclo de vida del Widget. Por tanto, necesitamos un StatefulWidget.
class FormularioSimple extends StatefulWidget {
@override
_FormularioState createState() => _FormularioState();
}
class _FormularioState extends State<FormularioSimple> {
// 1. Instanciamos el controlador
final TextEditingController _emailController = TextEditingController();
@override
void dispose() {
// 2. ¡IMPORTANTE! Limpiamos memoria al salir
_emailController.dispose();
super.dispose();
}
void _enviarDatos() {
// 3. Accedemos al texto actual con .text
print("Email introducido: ${_emailController.text}");
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: _emailController, // 4. Lo vinculamos al widget
decoration: InputDecoration(labelText: "Email"),
),
ElevatedButton(onPressed: _enviarDatos, child: Text("Enviar"))
],
);
}
}Ojo con la memoria: Un TextEditingController se queda escuchando eventos nativos. Si olvidas llamar a dispose() cuando la pantalla se cierra, crearás un Memory Leak (fuga de memoria).
Decoración: InputDecoration
El aspecto predeterminado depende del tema Material de la aplicación. Para añadir bordes, iconos y textos auxiliares, usamos la propiedad decoration con la clase InputDecoration.
Aquí están las propiedades que usarás el 90% de las veces:
TextField(
decoration: InputDecoration(
// Etiqueta que flota arriba al hacer foco
labelText: "Nombre de Usuario",
// Texto de ayuda (placeholder) que desaparece al escribir
hintText: "Ej: Luis Llamas",
// Icono a la izquierda
prefixIcon: Icon(Icons.person),
// Icono a la derecha (útil para borrar texto o ver password)
suffixIcon: Icon(Icons.check_circle),
// Borde completo (cajita)
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)
),
),
)Tipo de teclado y acción
No hay nada más frustrante que te pidan el teléfono y salga el teclado de letras completo.
Podemos (y debemos) ayudar al usuario configurando el tipo de teclado (keyboardType) y la acción del botón “Enter” (textInputAction).
TextField(
// Muestra teclado numérico, de email, de url, etc.
keyboardType: TextInputType.emailAddress,
// Cambia el botón "Enter" por "Siguiente", "Buscar", "Enviar"...
textInputAction: TextInputAction.next,
// Oculta el texto (puntitos) para contraseñas
obscureText: true,
)Gestión del foco con FocusNode
El “Foco” determina qué campo de texto está activo y recibiendo input del teclado.
A veces queremos controlar esto manualmente. El caso de uso típico es: “Cuando el usuario pulse ‘Siguiente’ en el campo Email, quiero que el foco salte automáticamente al campo Contraseña”.
Para esto usamos los FocusNode.
class FormularioAvanzado extends StatefulWidget {
@override
_FormularioAvanzadoState createState() => _FormularioAvanzadoState();
}
class _FormularioAvanzadoState extends State<FormularioAvanzado> {
// Nodos de foco
final FocusNode _nombreFocus = FocusNode();
final FocusNode _passFocus = FocusNode();
@override
void dispose() {
// También hay que limpiarlos
_nombreFocus.dispose();
_passFocus.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
focusNode: _nombreFocus, // Asignamos nodo 1
textInputAction: TextInputAction.next, // Botón "Siguiente"
onSubmitted: (_) {
// Al pulsar enter/siguiente, pedimos foco para el nodo 2
FocusScope.of(context).requestFocus(_passFocus);
},
),
TextField(
focusNode: _passFocus, // Asignamos nodo 2
),
],
);
}
}El truco para cerrar el teclado
Un comportamiento esperado en móviles es que, si toco en un espacio vacío fuera del teclado, este se cierre. En Flutter esto no pasa por defecto.
Tenemos que implementarlo nosotros envolviendo la pantalla en un GestureDetector:
GestureDetector(
onTap: () {
// Esto quita el foco de cualquier input, cerrando el teclado
FocusScope.of(context).unfocus();
},
child: Scaffold( ... ),
)