FutureBuilder — qué es, trabajo con Future en Flutter

Autor: IT Sectr Publicado: 2026-07-02 Tiempo de lectura: 8 min

FutureBuilder es un widget en Flutter que reconstruye automáticamente su interfaz basándose en el estado actual de AsyncSnapshot obtenido de un Future proporcionado. A diferencia de llamar manualmente a setState después de await, FutureBuilder proporciona un enfoque declarativo: se suscribe al Future en el primer renderizado y llama a la función builder en cada cambio de estado — carga, error o datos listos. Según la Referencia de la API de Flutter (2026), FutureBuilder es especialmente útil para cargar datos de la red, leer de una base de datos y cualquier operación asíncrona donde la UI deba mostrar un indicador de carga, mensaje de error o contenido listo.

Puntos Clave

  • FutureBuilder — widget de Flutter para construir UI basada en el estado de Future mediante AsyncSnapshot (none, waiting, active, done)
  • AsyncSnapshot — un objeto que contiene el estado actual de una operación asíncrona: connectionState, data y error
  • builder — una función callback invocada en cada cambio de estado del Future para reconstruir la UI
  • Manejo de errores — AsyncSnapshot.hasError permite mostrar una interfaz alternativa ante fallos en la operación asíncrona
  • ConnectionState — un enum con cuatro valores: none (sin operación), waiting (esperando), active (stream), done (completado)

¿Qué es FutureBuilder en Flutter?

FutureBuilder es un widget integrado de Flutter del paquete widgets que toma un Future y una función builder. Cuando el estado del Future cambia (ejecutándose, completado con datos, completado con error), FutureBuilder reconstruye automáticamente la UI llamando al builder con un nuevo AsyncSnapshot. Esto elimina la necesidad de gestionar manualmente el estado de carga mediante setState y banderas.

A diferencia de StreamBuilder, que trabaja con flujos de datos (Stream), FutureBuilder está diseñado para operaciones asíncronas únicas: solicitud HTTP, lectura de archivo, consulta a base de datos. FutureBuilder gestiona la suscripción al Future por sí mismo: en la primera construcción, inicia el Future y rastrea su finalización. Cuando el widget se destruye, FutureBuilder no cancela el Future — esa es responsabilidad del desarrollador.

Según el Flutter Cookbook (2026), se recomienda FutureBuilder para casos donde una operación asíncrona se ejecuta una vez al inicializar la pantalla. Para operaciones recurrentes o flujos de datos, use StreamBuilder. Ambos widgets siguen el mismo patrón de UI Reactiva, pero FutureBuilder está optimizado para solicitudes únicas.

Cómo funciona FutureBuilder internamente

La implementación interna de FutureBuilder se suscribe al Future usando Future.then y catchError. Al iniciar, FutureBuilder establece connectionState en ConnectionState.waiting y llama al builder con datos vacíos. Al completarse exitosamente, connectionState cambia a ConnectionState.done con datos. En caso de error, snapshot.error se llena con el objeto de error. Cada cambio desencadena una reconstrucción del widget.

AsyncSnapshot: estados y propiedades

AsyncSnapshot es un objeto contenedor que FutureBuilder pasa a la función builder en cada cambio de estado. Contiene toda la información sobre el estado actual de la operación asíncrona: si la carga está en progreso, qué datos se recibieron o si ocurrió un error. Comprender AsyncSnapshot es clave para construir correctamente la UI con FutureBuilder.

PropiedadTipoDescripción
connectionStateConnectionStateEstado actual de la conexión (none, waiting, active, done)
dataT?Datos recibidos del Future (null hasta completar o en error)
errorObject?Objeto de error si el Future finalizó con una excepción
hasDatabooltrue si data no es null y connectionState es ConnectionState.done
hasErrorbooltrue si el Future finalizó con un error

ConnectionState: cuatro estados de una operación asíncrona

El enum ConnectionState define la etapa de una operación asíncrona. None — estado inicial cuando el Future aún no se ha iniciado (rara vez usado, típicamente en la primera construcción sin initialData). Waiting — el Future se está ejecutando, datos aún no recibidos. Active — usado solo por StreamBuilder para flujos con datos parciales. Done — el Future ha finalizado, los datos están disponibles mediante snapshot.data o el error mediante snapshot.error.

El manejo adecuado de todos los estados de AsyncSnapshot en la función builder es un requisito obligatorio para el código de producción. Si no se maneja el estado waiting, el usuario verá una pantalla vacía durante la carga. Si no se maneja hasError, el usuario recibirá una Excepción sin explicación. El patrón recomendado: verificar hasError → verificar hasData → mostrar carga por defecto.

Patrones de uso de FutureBuilder

FutureBuilder se puede usar en varios patrones estándar, cada uno resolviendo una tarea específica. Veamos los escenarios principales: carga de datos al inicializar, carga con caché, solicitudes paralelas y manejo de errores con reintento.

Carga de datos al inicializar la pantalla

El patrón más común — FutureBuilder en el método build de un StatefulWidget o StatelessWidget. El Future se pasa desde initState o se crea directamente en build. Es importante no crear el Future en el método build en cada reconstrucción — esto provocará solicitudes repetidas. Use un Future almacenado en un campo del State.

Carga con caché y actualización

Para evitar solicitudes repetidas, FutureBuilder se puede combinar con CachedNetworkImage o una caché local. Después de la primera carga, los datos se guardan en memoria o SharedPreferences, y FutureBuilder muestra los datos en caché instantáneamente mientras los actualiza desde la red en paralelo. Esto mejora la experiencia de usuario con una respuesta instantánea.

Según pub.dev (2026), el almacenamiento en caché es especialmente relevante para imágenes y listas de datos. FutureBuilder con CachedNetworkImageProvider muestra automáticamente una imagen en caché, y cuando está ausente — un indicador de carga seguido del archivo descargado.

FutureBuilder vs setState: ¿qué elegir?

FutureBuilder y la gestión manual del estado mediante setState son dos enfoques para la UI asíncrona en Flutter. Cada uno tiene sus ventajas y limitaciones. La elección depende de la complejidad de la pantalla y la cantidad de operaciones asíncronas.

FutureBuilder gana en simplicidad: no es necesario declarar campos para el estado de carga, datos y error — todo se gestiona mediante AsyncSnapshot. Es ideal para pantallas simples con una operación asíncrona (una solicitud HTTP, lectura de base de datos). Sin embargo, con 5+ operaciones asíncronas en una pantalla, FutureBuilder crea un anidamiento excesivo — resultando en una “Pirámide” de FutureBuilders anidados.

setState con banderas de estado manuales da más control y legibilidad para lógica compleja. Para pantallas con múltiples solicitudes dependientes (cargar usuario → cargar sus pedidos → cargar detalles del pedido), es mejor usar setState con ChangeNotifier o Bloc. Según la Guía de Gestión de Estado de Flutter (2026), para escenarios complejos se recomienda Riverpod o Bloc en lugar de FutureBuilder, ya que proporcionan una mejor separación de la lógica y la presentación.

Ejemplo de FutureBuilder con carga de datos de red

Consideremos un ejemplo práctico de FutureBuilder para cargar una lista de usuarios desde una API REST. El código demuestra el manejo correcto de los tres estados de AsyncSnapshot: carga, error y datos listos.

dart
class UserListPage extends StatefulWidget {
  const UserListPage({super.key});

  @override
  State<UserListPage> createState() => _UserListPageState();
}

class _UserListPageState extends State<UserListPage> {
  final Future<List<User>> usersFuture = UserRepository().fetchUsers();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Usuarios')),
      body: FutureBuilder<List<User>>(
        future: usersFuture,
        builder: (context, AsyncSnapshot<List<User>> snapshot) {
          if (snapshot.hasError) {
            return Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  const Icon(Icons.error_outline, size: 48, color: Colors.red),
                  const SizedBox(height: 16),
                  Text('Error: ${snapshot.error}'),
                ],
              ),
            );
          }

          if (snapshot.hasData) {
            final users = snapshot.data!;
            return ListView.builder(
              itemCount: users.length,
              itemBuilder: (context, index) {
                return ListTile(
                  leading: CircleAvatar(backgroundImage: NetworkImage(users[index].avatarUrl)),
                  title: Text(users[index].name),
                  subtitle: Text(users[index].email),
                );
              },
            );
          }

          return const Center(child: CircularProgressIndicator());
        },
      ),
    );
  }
}

En el ejemplo, FutureBuilder maneja los tres estados. En caso de error, se muestra un icono con un mensaje de error. En carga exitosa — un ListView con avatares y nombres. Durante la carga — un CircularProgressIndicator. El Future se declara como un campo de clase, lo que evita la invocación repetida en reconstrucciones. Este patrón cubre el 90% de los escenarios de uso de FutureBuilder en aplicaciones móviles.

Preguntas Frecuentes

¿Por qué FutureBuilder llama al builder varias veces?

FutureBuilder llama al builder en cada cambio de estado del Future: la primera vez en la creación (connectionState: none o waiting), la segunda vez al completarse (connectionState: done). Si el widget padre se reconstruye, FutureBuilder también se reconstruye. Para evitar llamadas repetidas, asegúrese de que el Future se cree fuera del método build — de lo contrario, cada llamada a build creará un nuevo Future.

¿Cómo evitar una solicitud repetida al reconstruir?

Almacene el Future en un campo de StatefulWidget (en initState) o use memoización. Si el Future se crea dentro del método build, cada llamada a build creará un nuevo Future, y FutureBuilder reiniciará la operación asíncrona. Para StatelessWidget, use el paquete cached_future o widgets keep-alive para que el Future se ejecute una sola vez independientemente de las reconstrucciones.

¿En qué se diferencia FutureBuilder de StreamBuilder?

FutureBuilder está diseñado para operaciones asíncronas únicas (una solicitud HTTP, una lectura de base de datos). StreamBuilder trabaja con flujos de datos que pueden emitir múltiples valores a lo largo del tiempo (chat, actualizaciones de precio, geolocalización). StreamBuilder soporta ConnectionState.active para datos parciales, mientras que FutureBuilder solo soporta waiting y done.

¿Cómo usar FutureBuilder con múltiples Futures?

Para múltiples Futures paralelos, use Future.wait y pase el resultado a un solo FutureBuilder. Future.wait toma una lista de Futures y devuelve un Future — cuando todos los Futures se completan, el builder recibe un array de resultados. Para solicitudes secuenciales, use una cadena de Future.then dentro de un Future o FutureBuilders anidados (menos legible). Una alternativa es el paquete riverpod con AsyncValue para múltiples estados asíncronos.

¿Cómo cancelar un Future al salir de la pantalla?

FutureBuilder no cancela el Future automáticamente. Para cancelar, use CancelableOperation del paquete async o un mecanismo personalizado mediante una bandera cancelled en State. Establezca la bandera en dispose(), y verifíquela después de que el Future se complete antes de llamar a setState. Alternativamente, use el paquete riverpod con AutoDispose, que cancela automáticamente las operaciones asíncronas al salir de la pantalla.

Resumen

  • FutureBuilder — widget de Flutter para construcción declarativa de UI basada en el estado de Future mediante AsyncSnapshot (waiting, done, error)
  • AsyncSnapshot — contenedor con connectionState, data y error; esencial para el manejo correcto de todos los estados de la operación asíncrona
  • builder — callback con tres ramas: hasError (mostrar error), hasData (mostrar datos), default (indicador de carga)
  • FutureBuilder vs setState — FutureBuilder es más simple para una operación, setState con Bloc/Riverpod es mejor para lógica compleja con múltiples solicitudes
  • Prevención de solicitudes repetidas — el Future debe ser un campo del State, no lo cree en el método build para evitar reinicios en cada reconstrucción
  • Cancelación de Future — FutureBuilder no cancela el Future al hacer dispose; use CancelableOperation o una bandera de cancelación para evitar setState después de la destrucción
  • Múltiples Futures — para solicitudes paralelas use Future.wait con un solo FutureBuilder; para secuenciales — cadenas en un solo Future

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también