Introducción
Se nos habrá presentado seguramente el caso en el que tenemos un Widget el cual necesita datos para su construcción pero estos datos no puede ser adquiridos inmediatamente sino que van a demorarse más o menos tiempo su obtención. Una lectura de un fichero, consultas a base de datos, un request a una página de internet...en fin operaciones asíncronas que pueden demorarse.
Obviamente hasta que no tengamos el contenido que necesita el Widget no deberíamos presentarlo pero...y si este contenido se demora como debemos actuar?
Flutter en su API nos provee de un Widget llamado FutureBuilder que nos permite lidiar con estos problemillas "asíncronos" de una modo fácil.
FutureBuilder
Para un funcionamiento mínimo FutureBuilder necesita en su constructor dos parámetros.
- future
- builder
El parámetro future es una instancia Future con el valor necesitado por nuestro Widget.
El parámetro builder es un método que debemos sobreescribir que tiene dos parámetros context y snapshot. El segundo parámetro es el relevante. Es un representante de la clase AsyncSnapshot que contiene métodos de tipo bool hasData, hasError...útiles porque nos informan del estado de la operación asíncrona para que podamos reaccionar.
Lo vemos con un ejemplo. Simplemente es un Text que aparecerá en nuestra pantalla a los cinco segundos de iniciar la aplicación.
Cómo parámetro en future le suministramos un objeto Future (devuelto por el método getFutureData) que será el contenido del texto.
En el método asíncrono getFutureData deliberadamente introducimos un retardo de cinco segundos en la obtención del literal 'Hola!!'.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp>{
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getFutureData(),
builder: ((context, snapshot){
if(snapshot.hasData){
return _buildTextWidget(snapshot.data);
}else{
return CircularProgressIndicator(strokeWidth: 10,);
}
})
);
}
Future<String> getFutureData() async{
return await Future.delayed(Duration(seconds: 5), () {
return 'Hola!!!';
});
}
Widget _buildTextWidget(String data){
return Center(child: Directionality(textDirection: TextDirection.ltr,child:Text(data,textScaleFactor: 3)));
}
}
Nuestro builder es llamado por el sistema con los distintos estados de la operación asíncrona.
Nosotros simplemente cuando no tenemos datos presentamos un indicador de progreso circular y cuando tenemos datos efectivamente presentamos el texto.
En un contexto de producción deberíamos tener en cuenta el estado de error en nuestro builder
...
...
return FutureBuilder(
future: getFutureData(),
builder: ((context, snapshot){
if(snapshot.hasData){
//TODO Widget normal
}if(snapshot.hasError){
//TODO Widget de error
}else{
//TODO Widget de espera
}
})
);
...
...
FutureBuilder con datos iniciales
El código mostrado es de lo más básico para una utilización correcta de FutureBuilder pero puede que querramos mostrar siempre el mismo Widget y no otro alternativo durante la demora como en el ejemplo.
En este caso podemos usar el parámetro initialData
Hemos cambiado el código anterior eliminando del builder la opción de mostrar el Widget de progreso y añadiendo el parámetro initialData con un literal que será mostrado cuando los datos aún no han llegado.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp>{
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getFutureData(),
initialData: "Cargando...",
builder: ((context, snapshot){
if(snapshot.hasData){
return _buildTextWidget(snapshot.data);
}
})
);
}
Future<String> getFutureData() async{
return await Future.delayed(Duration(seconds: 5), () {
return 'Hola!!!';
});
}
Widget _buildTextWidget(String data){
return Center(child: Directionality(textDirection: TextDirection.ltr,child:Text(data,textScaleFactor: 3)));
}
}
Top comments (0)