Guía completa de Flutter: El widget TextField


Avatar de Pedro Cortez

El widget TextField es uno de los elementos fundamentales en una aplicación Flutter, permitiendo a tus usuarios ingresar texto y poder recuperarlo. En este tutorial, te explico cómo implementar este widget y aprovecharlo al máximo.


¿Para qué sirve el widget TextField en Flutter?

El widget TextField es un componente esencial en Flutter que permite a tus usuarios ingresar información en una barra de texto. Se puede utilizar tanto para la entrada de texto en un formulario como para crear campos de búsqueda interactivos. Puedes utilizarlo, por ejemplo, para:

  • Crear un formulario de inicio de sesión: El campo TextField permite al usuario ingresar su nombre de usuario o contraseña.
  • Agregar una barra de búsqueda en una aplicación: Se puede usar para filtrar datos según la entrada del usuario (como una barra de búsqueda en una aplicación de compras).
  • Tomar notas: En una aplicación para tomar notas, un TextField permite guardar textos de manera rápida e intuitiva.

Cómo integrar un TextField en Flutter

Una de las propiedades más importantes al implementar un TextField es el TextEditingController. Este widget se utiliza para controlar y acceder al texto que el usuario ingresa en el TextField. Permite:

  • Recuperar el texto ingresado.
  • Actualizar el texto en el campo de forma programática.
  • Escuchar las modificaciones del texto si es necesario.

Por lo tanto, es importante que cada TextField que crees tenga su propio TextEditingController. Cada campo de texto debe poder gestionar su propio estado de forma independiente, y compartir un solo controlador entre varios TextField puede llevar a comportamientos impredecibles.

Dicho esto, aquí tienes un ejemplo simple de implementación de un widget TextField en una aplicación Flutter:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Ejemplo Simple de TextField'),
        ),
        body: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // Crear un TextEditingController para el TextField
  TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          // TextField con un controlador
          TextField(
            controller: _controller,  // Asocia el controlador a este TextField
            decoration: InputDecoration(
              labelText: 'Ingresa tu texto',
            ),
          ),
          SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              // Muestra el texto ingresado en la consola
              print('Texto ingresado: ${_controller.text}');
            },
            child: Text('Mostrar texto en la consola'),
          ),
        ],
      ),
    );
  }
} 

¿Por qué un TextField siempre debe estar colocado en un StatefulWidget?

El widget TextField siempre debe estar integrado en un StatefulWidget porque es interactivo y el estado del texto que contiene puede cambiar con el tiempo, dependiendo de lo que el usuario ingrese.

Un StatelessWidget no permite gestionar estas modificaciones, ya que está diseñado para mostrar elementos estáticos e inmutables. En cambio, un StatefulWidget es capaz de mantener y actualizar su estado interno (como el texto ingresado), lo cual es esencial cuando deseas que el texto ingresado se almacene, modifique o recupere a través de un TextEditingController.

Por lo tanto, para que el texto del campo se actualice correctamente y puedas acceder a él, es imperativo utilizar un StatefulWidget.

Enviar una entrada a Firebase

La mayoría de las veces, querrás almacenar en algún lugar el texto ingresado por el usuario, por ejemplo, en tu base de datos Firebase. Aquí tienes un breve ejemplo de cómo enviar un mensaje y su hora de adición al back-end:

import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart'; // Importar Firestore

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('TextField a Firebase'),
        ),
        body: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // Creación del TextEditingController para el TextField
  TextEditingController _controller = TextEditingController();

  // Función para enviar los datos a Firestore
  Future<void> _sendDataToFirebase(String text) async {
    // Agrega el texto en la colección "messages" de Firestore
    await FirebaseFirestore.instance.collection('messages').add({
      'texto': text,  // Almacenar el texto ingresado
      'timestamp': FieldValue.serverTimestamp(),  // Agregar un sello de tiempo
    });
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          // TextField con un controlador para capturar el texto
          TextField(
            controller: _controller,
            decoration: InputDecoration(
              labelText: 'Ingresa un mensaje',
              border: OutlineInputBorder(),
            ),
          ),
          SizedBox(height: 20),
          // Botón para enviar los datos a Firestore
          ElevatedButton(
            onPressed: () {
              _sendDataToFirebase(_controller.text); // Enviar el texto a Firebase
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(content: Text('Datos enviados a Firebase')),
              );
              _controller.clear(); // Limpiar el campo después del envío
            },
            child: Text('Enviar a Firebase'),
          ),
        ],
      ),
    );
  }
}

Vaciar el contenido del TextField después de enviar los datos

Una vez que los datos se envían a Firebase, probablemente querrás que el TextField se vacíe de su contenido, en lugar de que el usuario tenga que borrarlo manualmente para poder escribir un nuevo mensaje.

Para ello, puedes utilizar el método _controller.clear(), que permite vaciar el contenido del TextField después de que se ha realizado una acción. Simplemente borrará el texto actualmente ingresado en el campo de texto, reemplazándolo por una cadena vacía («»).

Personalizar un TextField

El widget TextField se puede personalizar de varias maneras para mejorar la experiencia del usuario y adaptarlo a tus necesidades específicas. Aquí hay algunas de las propiedades más comunes y útiles para personalizar su comportamiento o diseño.

decoration

La propiedad decoration permite personalizar la apariencia del TextField. Generalmente, utiliza el widget InputDecoration, que ofrece muchas opciones como la etiqueta, el borde y los íconos. Por ejemplo:

TextField(
  decoration: InputDecoration(
    labelText: 'Nombre', // Texto que aparece encima del campo cuando el usuario comienza a escribir
    hintText: 'Ingresa tu nombre', // Texto de indicación dentro del campo
    border: OutlineInputBorder(), // Agrega un borde alrededor del TextField
    prefixIcon: Icon(Icons.person), // Agrega un ícono antes del texto
  ),
)

keyboardType

La propiedad keyboardType permite especificar el tipo de teclado que se mostrará cuando el usuario escriba en el TextField. Por ejemplo, si esperas una entrada numérica o una dirección de correo electrónico, puedes ajustar el teclado para que se corresponda.

TextField(
  keyboardType: TextInputType.emailAddress, // Muestra un teclado adaptado a correos electrónicos
  decoration: InputDecoration(
    labelText: 'Email',
  ),
)

Algunos valores comunes:

  • TextInputType.text: El teclado predeterminado para ingresar texto.
  • TextInputType.number: Para entradas numéricas.
  • TextInputType.emailAddress: Para ingresar una dirección de correo electrónico.

obscureText

Si deseas ocultar el texto ingresado en el TextField, por ejemplo, para una contraseña, puedes utilizar la propiedad obscureText.

TextField(
  obscureText: true, // Oculta el texto para los campos de contraseña
  decoration: InputDecoration(
    labelText: 'Contraseña',
  ),
)

Cuando obscureText está configurado en true, el texto se oculta con puntos o asteriscos por razones de privacidad. Puedes agregar un botón que cambie su estado a false, para mostrar el texto cuando el usuario haga clic en él.

maxLength

La propiedad maxLength te permite limitar el número de caracteres que el usuario puede ingresar en el campo de texto.

TextField(
  maxLength: 20, // Limita a 20 caracteres
  decoration: InputDecoration(
    labelText: 'Nombre de usuario',
  ),
)

onChanged

La propiedad onChanged permite ejecutar una función cada vez que el texto en el TextField cambia. Esto puede ser útil para validaciones en tiempo real o para actualizar la interfaz según lo que se ingrese.

TextField(
  onChanged: (text) {
    print('El texto ha cambiado: $text'); // Muestra el texto en cada cambio
  },
  decoration: InputDecoration(
    labelText: 'Búsqueda',
  ),
)

Definir la acción del teclado

La propiedad textInputAction permite especificar la acción del teclado que se muestra cuando el usuario interactúa con un campo de texto. Determina el tipo de botón que aparecerá en la barra de herramientas del teclado (como «Entrar», «Siguiente», «Atrás», etc.). Esta acción puede utilizarse para facilitar la navegación entre diferentes campos de texto o ejecutar acciones específicas.

Puedes definir esta propiedad asociándola a un valor de la enumeración TextInputAction. Los valores más comunes son TextInputAction.done, TextInputAction.next, TextInputAction.go, etc. Por ejemplo:

TextField(
  textInputAction: TextInputAction.next, // Pasa al siguiente campo cuando se presiona Enter
  onChanged: (text) {
    print('El texto es: $text');
  },
)

Las diferentes acciones posibles son las siguientes:

  • TextInputAction.done: Este botón indica que el usuario ha terminado de escribir. El botón puede mostrar «Terminar», «OK» o «Cerrar», dependiendo de la plataforma.
  • TextInputAction.go: Se utiliza cuando se debe realizar una acción, como enviar un formulario o hacer una búsqueda. En el botón, generalmente veremos «Go», «Lanzar» o «Ejecutar».
  • TextInputAction.next: Es la acción para pasar al siguiente campo en un formulario. El botón generalmente mostrará «Siguiente».
  • TextInputAction.previous: Se utiliza para volver al campo anterior. El botón a menudo muestra «Anterior» o «Volver».
  • TextInputAction.search: Esta acción se usa frecuentemente en campos de búsqueda. El botón puede mostrar «Buscar», «Encontrar» o, a veces, el ícono de la lupa.
  • TextInputAction.send: Se utiliza para enviar un mensaje en aplicaciones de mensajería. El botón será a menudo «Enviar» o, a veces, un ícono de envío, como una flecha.

Realizar una acción al enviar el campo

La propiedad onFieldSubmitted permite definir una función de devolución de llamada (callback) que se ejecutará cuando el usuario envíe un campo de texto, generalmente al presionar la tecla «Enter» o el botón definido por textInputAction. Esto puede ser muy útil para validar un formulario, enviar una solicitud o pasar al siguiente campo.

Puedes asociar una función a onFieldSubmitted para realizar una acción cuando el usuario envíe el campo. Por ejemplo:

TextField(
  onFieldSubmitted: (String value) {
    print('El texto enviado es: $value');
    // Realizar una acción como enviar una solicitud o validar un formulario
  },
)

Un caso concreto sería si tienes un formulario con varios campos de texto. Puedes utilizar onFieldSubmitted para que, cuando un usuario termine de completar un campo, se redirija automáticamente al siguiente campo:

TextField(
  textInputAction: TextInputAction.next,  // Pasa al siguiente campo
  onFieldSubmitted: (String value) {
    FocusScope.of(context).nextFocus();  // Mueve el foco al siguiente campo
  },
)

Conclusión

El TextField es un widget muy poderoso que se utilizará en muchas aplicaciones. Permitir al usuario ingresar información y almacenarla en algún lugar para poder mostrarla en tiempo real es la base de casi todas las aplicaciones móviles.

Ahora que sabes cómo utilizar los widgets básicos de Flutter, ¿por qué no hacer que tu aplicación sea más dinámica utilizando la solución de back-end Firebase?

Avatar de Pedro Cortez