Flutter de Google desde cero: cuadrículas, listas y fuentes de datos
() translation by (you can also view the original English article)
Actualmente, casi todas las aplicaciones móviles no triviales probablemente tienen listas en sus diseños. Esto se debe a que el uso de una lista desplazable es a menudo la forma más fácil de mostrar un gran número de elementos similares en una pantalla pequeña.
El framework Flutter ofrece varios widgets que puedes utilizar para crear y mostrar dichas listas de modo eficiente y con un código mínimo. En este tutorial, te enseñaré cómo usarlos con fuentes de datos locales y remotas.
1. Mostrando una lista no desplazable
Si necesitas mostrar un pequeño número de elementos similares, y estás seguro de que la pantalla del usuario podrá acomodarlos todos al mismo tiempo, usar el widget Column
es lo más eficiente.
Para crear una lista en tu aplicación de Flutter, puedes usar la clase List
que ofrece el lenguaje de programación Dart. Después de crear la lista, puedes utilizar su método add()
para añadirle cualquier número de elementos. El siguiente código te muestra cómo crear una lista que contiene tres widgets RaisedButton
:
1 |
// Create list
|
2 |
List<RaisedButton> myItems = new List(); |
3 |
|
4 |
// Add three button widgets to it
|
5 |
myItems.add(new RaisedButton( |
6 |
child: new Text("Twitter"), |
7 |
onPressed: (){} |
8 |
));
|
9 |
myItems.add(new RaisedButton( |
10 |
child: new Text("Facebook"), |
11 |
onPressed: (){} |
12 |
));
|
13 |
myItems.add(new RaisedButton( |
14 |
child: new Text("Reddit"), |
15 |
onPressed: (){} |
16 |
));
|
Ten en cuenta que cada elemento de la lista tiene un controlador de eventos onPressed
vacío asociado porque, sin eso, el elemento estaría deshabilitado.
Ahora que la lista está preparada, puedes asignarla directamente a la propiedad children
del widget Column
que se mostrará. Sin embargo, generalmente también tendrás que especificar dónde se deben colocar los elementos de la lista en la pantalla. Debido a que el widget Column
es un widget flexible, puedes controlar las posiciones de los elementos a lo largo de su eje principal y eje transversal con las propiedades mainAxisAlignment
y crossAxisAlignment
. Para el widget Column
, de manera predeterminada, el eje principal es el eje vertical y el eje transversal es el eje horizontal.
El siguiente código te muestra cómo expandir los tres botones a lo largo del eje horizontal y colocarlos en el centro de la pantalla verticalmente:
1 |
Column column = new Column( |
2 |
mainAxisAlignment: MainAxisAlignment.center, |
3 |
crossAxisAlignment: CrossAxisAlignment.stretch, |
4 |
children: myItems |
5 |
);
|
Esta es la apariencia que tendrá la columna ahora:



Es importante tener en cuenta que encontrarás un error de tiempo de ejecución si un widget de Column
no puede acomodar a todos sus elementos children. Por ejemplo, si tuvieras más de una docena de widgets RaisedButton
en tu lista en lugar de tres, verás un mensaje de error igual a este:



2. Mostrando una lista desplazable simple
Para listas un poco más grandes, listas cuyo contenido probablemente desborde la pantalla, debes considerar el uso de un widget ListView
porque admite el desplazamiento.
Tal vez hayas notado que el código que escribimos para crear la lista en el paso anterior era largo y repetitivo. Crear una lista más grande con el mismo método puede resultar muy tedioso. Un enfoque alternativo fácil, es utilizar dos listas: una que contenga los datos y otra que contenga los widgets.
Así es como puedes usar el operador []
para crear rápidamente una lista de datos que, por ahora, solamente contiene varias strings:
1 |
List<String> data = <String>["Twitter", "Reddit", "YouTube", "Facebook", |
2 |
"Vimeo", "GitHub", "GitLab", "BitBucket", "LinkedIn", "Medium", |
3 |
"Tumblr", "Instagram", "Pinterest"]; |
Para convertir la lista anterior de strings en una lista de widgets RaisedButton
, puedes usar los métodos map()
y toList()
. Con el método map()
, puedes usar cada string para generar un nuevo widget RaisedButton
. Y con el método toList ()
, puedes convertir el objeto Iterable
devuelto por el método map()
en un objeto List
real. El siguiente código te muestra cómo:
1 |
List<RaisedButton> myWidgets = data.map((item) { |
2 |
return new RaisedButton( |
3 |
child: new Text(item), |
4 |
onPressed: () async { |
5 |
String url = "https://${item}.com"; |
6 |
if(await canLaunch(url)) |
7 |
await launch(url); |
8 |
}
|
9 |
);
|
10 |
}).toList(); |
Con el fin de cubrir cada aspecto, el código anterior también te muestra cómo crear un controlador de eventos onPressed
que utiliza los métodos canLaunch()
y launch()
que ofrece el paquete url_launcher
para abrir el sitio web que el usuario seleccionó en el navegador predeterminado.
Una vez que tu lista esté preparada, puedes pasarla a la propiedad children
del widget ListView
para mostrarla.
1 |
ListView myList = new ListView( |
2 |
children: myWidgets |
3 |
);
|
En este punto, si ejecutas la aplicación, deberías poder desplazarte por la lista y presionar cualquier botón para iniciar el sitio web asociado.



3. Creando una cuadrícula
El widget ListView
te permite colocar solamente un elemento en su eje transversal. De manera predeterminada, el artículo se expandirá para ocupar todo el espacio disponible en ese eje. Si deseas más flexibilidad, deberías considerar el uso de un widget GridView
, el cual te permite especificar cuántos elementos quieres en el eje transversal.
El siguiente código utiliza el constructor GridView.count()
para crear un widget GridView
que muestra dos elementos por fila:
1 |
GridView myGrid = GridView.count( |
2 |
crossAxisCount: 2, |
3 |
children: myWidgets |
4 |
);
|
Este es el aspecto que tiene la cuadrícula:



4. Mostrando listas grandes
Para las listas de datos que contienen más de algunas docenas de elementos, debes evitar generar listas de widgets manualmente, como lo hiciste en un paso anterior. ¿Por qué? Porque crear un widget es una operación costosa y las listas grandes de widgets pueden consumir mucha memoria.
En lugar de ello, debe utilizar la función IndexedWidgetBuilder
, la cual te permite generar widgets solamente cuando el usuario necesite verlos. Con ella, puedes generar widgets de forma «lazy» a medida que el usuario se desplace por tu widget ListView
.
Es muy poco probable que vayas a tener grandes cantidades de datos definidos dentro de tus aplicaciones. Por lo general, estos datos los obtendrías de un servidor remoto. Por tal motivo, para ofrecerte un ejemplo realista, permíteme mostrarte cómo obtener 100 preguntas de Stack Overflow usando la API de Stack Exchange y mostrarlas tan pronto como se requieran.
Empieza creando una subclase de la clase StatefulWidget
, que actuará como un contenedor para tu widget ListView
y anulará su método createState()
.
1 |
class VeryLargeListHolder extends StatefulWidget { |
2 |
@override
|
3 |
State<StatefulWidget> createState() { |
4 |
return new MyState(); |
5 |
}
|
6 |
}
|
La clase MyState
mencionada en el código anterior todavía no existe, así que créala y anula su método build()
.
1 |
class MyState extends State<VeryLargeListHolder> { |
2 |
@override
|
3 |
Widget build(BuildContext context) { |
4 |
// TODO
|
5 |
}
|
6 |
}
|
Luego, añade un objeto List
como una de las variables miembro de la clase. Lo usarás para almacenar las preguntas que descargaste de Stack Overflow. Asimismo, agrega el endpoint de la API como otra variable.
1 |
List questions; |
2 |
|
3 |
String endpoint = "https://api.stackexchange.com/2.2/questions?" + |
4 |
"pagesize=100&order=desc&sort=activity&site=stackoverflow"; |
A menos que quieras que tu usuario presione un botón para descargar las preguntas, te sugiero que las descargues automáticamente cuando el widget se esté inicializando. Por consiguiente, anula el método initState()
y realiza una llamada a un nuevo método asincrónico llamado loadData()
.
1 |
@override
|
2 |
void initState() { |
3 |
super.initState(); |
4 |
loadData(); |
5 |
}
|
6 |
|
7 |
void loadData() async { |
8 |
// More code here
|
9 |
}
|
Dentro del método loadData()
, puedes utilizar la función get()
del paquete http
de Dart para descargar las preguntas. El endpoint de la API devuelve un documento JSON, que puedes «parsear» con la función json.decode()
disponible en el paquete convert
de Dart. El siguiente código te muestra cómo:
1 |
String rawData = (await http.get(endpoint)).body; |
2 |
Map jsonData = json.decode(rawData); |
Una vez que el documento JSON ha sido convertido en un objeto Map
, puedes usar el valor asociado con su clave items
para inicializar la variable questions
. No obstante, la variable es parte del estado del widget. Por lo tanto, debes asegurare de actualizarlo solo dentro del método setState()
. Aquí tienes cómo:
1 |
setState(() { |
2 |
questions = jsonData["items"]; |
3 |
});
|
En este punto, puedes crear un nuevo widget ListView
usando el constructor ListView.builder()
, que espera una función IndexedWidgetBuilder
y un conteo de elementos como argumentos. Por ahora, el conteo de elementos no es más que el tamaño de la lista questions
. Por lo tanto, añade el siguiente código dentro del método build()
de la clase MyState
:
1 |
ListView myList = ListView.builder( |
2 |
itemCount: questions == null ? 0 : questions.length, |
3 |
itemBuilder: (BuildContext context, int index) { |
4 |
// More code here
|
5 |
}
|
6 |
);
|
Dentro de la función del constructor, todo lo que necesitas hacer es crear un pequeño árbol de widgets para mostrar varios detalles acerca de cada pregunta que descargaste. El paquete material
de Flutter ofrece un widget muy útil llamado ListTile
, que te permite crear rápidamente un árbol de este tipo mientras se adhiere a las directrices de Material Design.
El siguiente código te muestra cómo desplegar el título y el autor de la pregunta, usando las propiedades title
y subtitle
del widget ListTile
:
1 |
return new ListTile( |
2 |
title: Text(questions[index]["title"]), |
3 |
subtitle: Text("Asked by ${questions[index]["owner"]["display_name"]}") |
4 |
);
|
Para concluir, crea un nuevo widget Scaffold
, asigna el widget ListView
a su propiedad body
y devuélvelo desde el método build()
para que pueda usarse con un widget MaterialApp
. Opcionalmente, puedes añadir un widget AppBar
al widget Scaffold
.
1 |
return new Scaffold( |
2 |
appBar: new AppBar( |
3 |
title: new Text("LargeListDemo") |
4 |
),
|
5 |
body: myList |
6 |
);
|
Así es como se verá la aplicación después de haber descargado las preguntas



Conclusión
Ahora ya sabes cómo trabajar con listas en una aplicación de Flutter. En este tutorial, aprendiste no solo a crear listas y cuadrículas que soporten grandes fuentes de datos, sino también cómo hacer que cada elemento dentro de ellas sea interactivo. Para aprender más sobre las listas en Flutter, puedes consultar la documentación oficial.