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 es un widget integrado de Flutter del paquete widgets que toma un Future
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.
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 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.
| Propiedad | Tipo | Descripción |
|---|---|---|
| connectionState | ConnectionState | Estado actual de la conexión (none, waiting, active, done) |
| data | T? | Datos recibidos del Future (null hasta completar o en error) |
| error | Object? | Objeto de error si el Future finalizó con una excepción |
| hasData | bool | true si data no es null y connectionState es ConnectionState.done |
| hasError | bool | true si el Future finalizó con un error |
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.
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.
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.
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 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.
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.
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
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.
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.
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.
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.
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
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.
Lea también