ListView.builder en Flutter
Crea listas eficientes y dinámicas con renderizado lazy
Descargar Ejemplos Completos¿Qué es ListView.builder?
El ListView.builder es un constructor de ListView que crea elementos bajo demanda. Es extremadamente eficiente para listas largas porque solo renderiza los elementos visibles.
ListView Normal
Renderiza todos los items al mismo tiempo
ListView.builder
Renderiza solo los items visibles
Sintaxis Básica
Constructor Básico de ListView.builder
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index]),
);
},
)
Características Principales:
- Renderizado lazy: Solo crea los elementos visibles
- Eficiente con listas largas: Ideal para 100+ elementos
- Dinámico: Se adapta a cambios en los datos
- Personalizable: Control total sobre cada item
Parámetros Principales
itemCount
Número total de elementos en la lista
itemCount: myList.length
itemBuilder
Función que construye cada elemento
itemBuilder: (context, index) {
return MyWidget(item[index]);
}
scrollDirection
Dirección del scroll (vertical/horizontal)
scrollDirection: Axis.vertical
¿Cuándo usar ListView.builder?
Listas Largas
Listas con muchos elementos (100+) donde el rendimiento es crucial
// Lista de 1000 usuarios
ListView.builder(
itemCount: users.length,
itemBuilder: (ctx, i) => UserCard(users[i]),
)
Datos Dinámicos
Listas que cambian frecuentemente o se cargan desde API
// Lista de noticias en tiempo real
ListView.builder(
itemCount: news.length,
itemBuilder: (ctx, i) => NewsItem(news[i]),
)
Apps Móviles
Aplicaciones que muestran feeds, chats, o cualquier lista extensa
// Feed de redes sociales
ListView.builder(
itemCount: posts.length,
itemBuilder: (ctx, i) => PostCard(posts[i]),
)
Ejemplos Visuales
1. Lista Básica con ListTile
ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
child: Text(users[index].name[0]),
),
title: Text(users[index].name),
subtitle: Text(users[index].email),
trailing: Icon(Icons.chevron_right),
onTap: () => navigateToUser(users[index]),
);
},
)
2. Lista Horizontal
ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categories.length,
itemBuilder: (context, index) {
return Container(
margin: EdgeInsets.all(8),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(20),
),
child: Text(categories[index]),
);
},
)
3. Lista con Items Personalizados
ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
return Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(products[index].name),
Text('\$${products[index].price}'),
],
),
SizedBox(height: 8),
Text(products[index].description),
SizedBox(height: 8),
Text('★' * products[index].rating),
],
),
),
);
},
)
Propiedades del ListView.builder
| Propiedad | Tipo | Descripción | Requerido |
|---|---|---|---|
| itemCount | int | Número total de elementos en la lista | Opcional* |
| itemBuilder | IndexedWidgetBuilder | Función que construye cada elemento | Sí |
| scrollDirection | Axis | Dirección del scroll (vertical/horizontal) | No |
| padding | EdgeInsets | Espacio alrededor de la lista | No |
| physics | ScrollPhysics | Comportamiento del scroll | No |
| shrinkWrap | bool | Si la lista debe ajustar su tamaño | No |
Nota sobre itemCount:
Si no se proporciona itemCount, la lista será infinita. Esto es útil para listas que se cargan progresivamente o que tienen un número desconocido de elementos.
Generador de ListView.builder
// El código se generará aquí
ListView.builder vs Alternativas
| Widget | Ventajas | Desventajas | Cuándo usar |
|---|---|---|---|
| ListView.builder | Eficiente, lazy loading, dinámico | Más complejo para listas simples | Listas largas o dinámicas |
| ListView | Simple, directo | Renderiza todos los items | Listas cortas y estáticas |
| Column | Máximo control, flexible | No scroll automático, ineficiente | Número fijo y pequeño de widgets |
| GridView.builder | Eficiente para grids | Complejidad adicional | Listas en grid/columnas |
Ejercicios Prácticos
Ejercicio 1: Lista de Tareas
Crea una lista de tareas donde cada item muestre el título y estado de completado.
Solución:
ListView.builder(
itemCount: tasks.length,
itemBuilder: (context, index) {
return CheckboxListTile(
title: Text(tasks[index].title),
value: tasks[index].completed,
onChanged: (bool? value) {
setState(() {
tasks[index].completed = value!;
});
},
);
},
)
Ejercicio 2: Lista Infinita
Crea una lista que cargue más elementos cuando el usuario llegue al final.
Solución:
ListView.builder(
itemCount: items.length + 1,
itemBuilder: (context, index) {
if (index == items.length) {
// Mostrar loading al final
return Center(child: CircularProgressIndicator());
}
return ListTile(title: Text(items[index]));
},
)
Ejercicio 3: Lista con Diferentes Tipos
Crea una lista que muestre diferentes tipos de widgets según el índice.
Solución:
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
if (index % 3 == 0) {
return HeaderItem(items[index]);
} else {
return ContentItem(items[index]);
}
},
)
Consejos y Mejores Prácticas
✅ Lo que SÍ debes hacer
- Usa keys únicas para items que pueden cambiar
- Implementa paginación para listas muy largas
- Usa const constructores en itemBuilder cuando sea posible
- Considera usar ScrollController para control avanzado
❌ Lo que NO debes hacer
- No uses setState dentro de itemBuilder
- Evita cálculos pesados en itemBuilder
- No olvides el itemCount en listas infinitas
- No uses ListView.builder para listas muy cortas
Tip Profesional
Para listas extremadamente largas, considera usar ListView.separated() que permite definir un separador personalizado entre items, mejorando el rendimiento y la apariencia.