¿Para qué sirve el widget FutureBuilder?
Como vimos en el artículo anterior, es común tener que manejar datos asincrónicos. Esto sucede, por ejemplo, al recuperar información desde una API, acceder a una base de datos o realizar operaciones costosas en segundo plano. Pero, ¿cómo mostrar esos datos en la interfaz de usuario de manera fluida teniendo en cuenta el tiempo que tardan en recuperarse? Aquí es donde entra en juego el widget FutureBuilder.
El FutureBuilder es una herramienta poderosa en Flutter que permite esperar la finalización de una tarea asincrónica y mostrar el contenido correspondiente al estado de esa tarea. Se basa en el concepto de Future, que representa un dato que estará disponible en algún momento… en el futuro.
El FutureBuilder le permite construir interfaces de usuario basadas en el estado del Future:
- Mientras el Future está en ejecución (estado pendiente), se puede mostrar un indicador de carga (como un círculo de progreso).
- Una vez que el Future se resuelve con un resultado (estado completado), se pueden mostrar los datos recuperados.
- En caso de error (estado fallido), también es posible mostrar un mensaje o una interfaz de error.
¿Cuándo debería usar el widget FutureBuilder?
El FutureBuilder es útil para mostrar contenido cuando necesita realizar una operación asincrónica puntual, es decir, cuando desea recuperar un dato una sola vez. Puede usarlo en escenarios donde una tarea termina con un único resultado final, como recuperar datos desde una API, realizar una consulta a una base de datos o cargar un archivo.
Por ejemplo, puede usarlo para:
- Realizar una llamada única a una API o base de datos (por ejemplo, cargar el perfil de un usuario o mostrar una lista de artículos).
- Cargar archivos locales, realizar un cálculo pesado en segundo plano y mostrar el resultado una vez finalizado.
- Mostrar un resultado por defecto si los datos no se cargan después de la recuperación inicial.
El FutureBuilder es perfecto para tareas puntuales que devuelven un único resultado. Sin embargo, si necesita reaccionar a flujos continuos de datos o eventos que ocurren repetidamente, el StreamBuilder, que presentaré en el próximo artículo, será más adecuado.
Ejemplo de implementación de un FutureBuilder en Flutter
A continuación, se muestra un ejemplo simple del uso del widget FutureBuilder, donde se recupera un nombre y un correo electrónico desde un documento en Firebase para mostrarlos en pantalla:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:tutoflutter/firebase_options.dart';
void main() async {
// Inicialización de Firebase
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FutureBuilder Firebase Demo',
home: UserScreen(),
);
}
}
class UserScreen extends StatelessWidget {
// Función asincrónica para obtener datos del usuario desde Firebase
Future<Map<String, dynamic>?> fetchUserData() async {
try {
// Recuperación del documento 'User1' en la colección 'Users'
DocumentSnapshot doc = await FirebaseFirestore.instance
.collection('Users')
.doc('User1')
.get();
return doc.data() as Map<String, dynamic>?;
} catch (e) {
print('Error al recuperar los datos: $e');
return null;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Usuario Firebase'),
),
body: Center(
// Uso del FutureBuilder para manejar el estado asincrónico
child: FutureBuilder<Map<String, dynamic>?>(
future: fetchUserData(), // Llamada a la función para recuperar los datos
builder: (context, snapshot) {
// Mientras se cargan los datos, se muestra un spinner
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
// En caso de error, se muestra un mensaje de error
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
// Si los datos están disponibles, se muestran en pantalla
if (snapshot.hasData) {
var userData = snapshot.data!;
return Text(
'Nombre: ${userData['name']}\nEmail: ${userData['email']}',
style: TextStyle(fontSize: 20),
textAlign: TextAlign.center,
);
}
// Si no hay datos, se muestra un mensaje predeterminado
return Text('No hay datos disponibles');
},
),
),
);
}
}
Explicación del código:
- Inicialización de Firebase: Antes de usar Firestore, Firebase se inicializa en
main()
conFirebase.initializeApp()
. - fetchUserData: Esta función asincrónica obtiene un documento específico de la colección
Users
en Firestore. Si el documento se encuentra, devuelve los datos como unMap
. En caso de error, devuelvenull
. - FutureBuilder:
- future: Representa la llamada a la función
fetchUserData()
. - builder: Genera la interfaz de usuario basada en el estado del snapshot:
- Pendiente: Se muestra un indicador de carga.
- Error: Se muestra un mensaje de error.
- Datos recuperados: Se muestran los datos del usuario (nombre y correo electrónico).
- future: Representa la llamada a la función
¿Por qué debo definir un tipo en mi FutureBuilder?
En el ejemplo, el FutureBuilder tiene el tipo <Map<String, dynamic>?>
. Esto indica que los datos serán un Map
con claves de tipo String
y valores de tipo dinámico (dynamic
).
El signo de interrogación (?
) indica que el Future puede devolver un valor nulo si no existe el documento o si ocurre un error en la solicitud.
Definir el tipo de datos recuperados asegura una gestión estricta y clara de los datos esperados, mejorando la estabilidad y claridad del código. Aunque no es obligatorio, es altamente recomendable.
Conclusión
Con el widget FutureBuilder, ahora puede recuperar datos en línea de manera puntual y mostrarlos en su aplicación Flutter.
¿Pero cómo mostrar contenido en tiempo real de forma dinámica? Por ejemplo, ¿cómo actualizar una lista de compras al agregar o eliminar elementos? Esto se logra con el widget StreamBuilder, que exploraremos en el próximo artículo.
StreamBuilder en Flutter: ¿Cuándo y por qué usarlo? →