ListView.Builder en Flutter

Crea listas eficientes, dinámicas y de alto rendimiento

Descargar Ejemplos Completos

¿Qué es ListView.Builder?

El widget ListView.Builder en Flutter es la forma más eficiente de crear listas con muchos elementos, ya que construye los widgets bajo demanda según se necesitan.

ListView Básico

  • ❌ Construye todos los items al mismo tiempo
  • ❌ Consumo alto de memoria
  • ❌ Lento con muchos elementos
  • ❌ No escalable

ListView.Builder

  • ✅ Construye items bajo demanda
  • ✅ Memoria optimizada
  • ✅ Rápido incluso con miles de items
  • ✅ Altamente escalable

Ventajas de ListView.Builder:

  • Lazy Loading: Solo construye los elementos visibles
  • Alto Rendimiento: Ideal para listas grandes
  • Memoria Optimizada: No carga todos los datos a la vez
  • Dinámico: Fácil de actualizar y modificar
  • Flexible: Personalización total de cada item

¿Cuándo usar ListView.Builder?

Listas Grandes

Cuando tienes muchos elementos (50+)

Datos Dinámicos

Cuando los datos vienen de APIs o bases de datos

Contenido Cambiante

Cuando la lista puede crecer o cambiar

Apps de Alto Rendimiento

Cuando necesitas optimizar el rendimiento

Implementación Básica

ListView.Builder básico con lista de elementos

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ListView.Builder Example',
      home: const ListViewExample(),
    );
  }
}

class ListViewExample extends StatelessWidget {
  const ListViewExample({super.key});

  // Lista de datos de ejemplo
  final List items = [
    'Manzana', 'Banana', 'Naranja', 'Uva', 'Fresa',
    'Piña', 'Mango', 'Pera', 'Kiwi', 'Sandía',
    'Melón', 'Durazno', 'Cereza', 'Limón', 'Lima'
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Lista de Frutas'),
        backgroundColor: Colors.green,
      ),
      body: ListView.builder(
        itemCount: items.length, // Número total de elementos
        itemBuilder: (context, index) {
          // Este builder se llama solo para los items visibles
          final fruit = items[index];
          final color = index % 2 == 0 ? Colors.green[50] : Colors.white;
          
          return Container(
            margin: const EdgeInsets.symmetric(vertical: 2, horizontal: 8),
            decoration: BoxDecoration(
              color: color,
              borderRadius: BorderRadius.circular(8),
            ),
            child: ListTile(
              leading: CircleAvatar(
                backgroundColor: Colors.green[100],
                child: Text(
                  '${index + 1}',
                  style: const TextStyle(
                    fontWeight: FontWeight.bold,
                    color: Colors.green,
                  ),
                ),
              ),
              title: Text(
                fruit,
                style: const TextStyle(
                  fontSize: 16,
                  fontWeight: FontWeight.w500,
                ),
              ),
              subtitle: Text('Fruta número ${index + 1}'),
              trailing: const Icon(Icons.chevron_right, color: Colors.green),
              onTap: () {
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(
                    content: Text('Seleccionaste: $fruit'),
                    duration: const Duration(seconds: 1),
                  ),
                );
              },
            ),
          );
        },
      ),
    );
  }
}

Propiedades de ListView.Builder

itemCount

Número total de elementos en la lista

Tipo: int?

Requerido: No (pero recomendado)

Por defecto: null (lista infinita)

itemBuilder

Función que construye cada elemento de la lista

Tipo: Widget? Function(BuildContext, int)

Requerido:

scrollDirection

Dirección del desplazamiento

Tipo: Axis

Valores: Axis.vertical, Axis.horizontal

Por defecto: Axis.vertical

padding

Espaciado interno de la lista

Tipo: EdgeInsets?

Por defecto: null

physics

Comportamiento físico del scroll

Tipo: ScrollPhysics?

Valores comunes: AlwaysScrollableScrollPhysics(), BouncingScrollPhysics(), NeverScrollableScrollPhysics()

shrinkWrap

Si la lista debe ajustarse a su contenido

Tipo: bool

Por defecto: false

Uso: Para listas dentro de Column

Ejemplos Visuales

1. Lista Básica con Diferentes Estilos

Lista de Tareas
1
Comprar víveres
Supermercado
2
Reunión de trabajo
10:00 AM
3
Gimnasio
6:00 PM
ListView.builder(
  itemCount: tasks.length,
  itemBuilder: (context, index) {
    final task = tasks[index];
    return AnimatedContainer(
      duration: Duration(milliseconds: 300),
      margin: EdgeInsets.symmetric(vertical: 4, horizontal: 8),
      decoration: BoxDecoration(
        color: index % 2 == 0 ? Colors.blue[50] : Colors.white,
        borderRadius: BorderRadius.circular(8),
        boxShadow: [
          BoxShadow(
            color: Colors.black12,
            blurRadius: 2,
            offset: Offset(0, 1),
          ),
        ],
      ),
      child: ListTile(
        leading: CircleAvatar(
          backgroundColor: Colors.blue[100],
          child: Text('${index + 1}'),
        ),
        title: Text(
          task.title,
          style: TextStyle(fontWeight: FontWeight.w500),
        ),
        subtitle: Text(task.subtitle),
        trailing: Icon(
          task.completed ? Icons.check_circle : Icons.radio_button_unchecked,
          color: task.completed ? Colors.green : Colors.grey,
        ),
        onTap: () => _toggleTask(index),
      ),
    );
  },
)

2. Lista Horizontal con Tarjetas

Productos Destacados
📱
iPhone 14
\$999
💻
MacBook Pro
\$1999
Apple Watch
\$399
ListView.builder(
  scrollDirection: Axis.horizontal,
  itemCount: products.length,
  padding: EdgeInsets.all(16),
  itemBuilder: (context, index) {
    final product = products[index];
    return Container(
      width: 150,
      margin: EdgeInsets.only(right: 16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        boxShadow: [
          BoxShadow(
            color: Colors.black12,
            blurRadius: 6,
            offset: Offset(0, 3),
          ),
        ],
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(product.icon, size: 48, color: Colors.blue),
          SizedBox(height: 12),
          Text(
            product.name,
            style: TextStyle(
              fontWeight: FontWeight.bold,
              fontSize: 16,
            ),
            textAlign: TextAlign.center,
          ),
          SizedBox(height: 8),
          Text(
            '\$${product.price}',
            style: TextStyle(
              color: Colors.green,
              fontWeight: FontWeight.bold,
              fontSize: 18,
            ),
          ),
        ],
      ),
    );
  },
)

Demo Interactivo de ListView.Builder

20 items
60px
8px

Demo ListView.Builder

0 items visibles

// El código se generará aquí

Mejores Prácticas

✅ Lo que SÍ debes hacer

  • Usa itemCount para listas con tamaño conocido
  • Mantén los itemBuilders simples y eficientes
  • Usa const widgets cuando sea posible
  • Considera usar ListView.separated para separadores
  • Optimiza las imágenes con cache y tamaño apropiado

❌ Lo que NO debes hacer

  • No crees widgets complejos en el itemBuilder
  • No uses setState dentro del itemBuilder
  • No olvides el itemCount en listas grandes
  • No uses ListView.builder para listas pequeñas fijas
  • No cargues datos pesados en el builder

Tip Profesional

Para listas extremadamente grandes (1000+ items), considera usar ListView.builder con addAutomaticKeepAlives: false y addRepaintBoundaries: false para máximo rendimiento, pero solo si no necesitas preservar el estado de los items.

Ejercicios para Practicar

Ejercicio 1: App de Contactos

Crea una lista de contactos con búsqueda en tiempo real.

Características: Scroll rápido, búsqueda, agrupación por letra

Ejercicio 2: Feed de Noticias

Implementa un feed infinito que cargue más contenido al hacer scroll.

Características: Scroll infinito, diferentes tipos de items, pull-to-refresh

Ejercicio 3: Carrito de Compras

Crea un carrito de compras con items que se pueden modificar.

Características: Items editables, total dinámico, animaciones

Solución de Problemas Comunes

❌ Error: "Vertical viewport was given unbounded height"

Solución: Envuelve el ListView en un Expanded o usa shrinkWrap: true

// En un Column Expanded( child: ListView.builder(...) ) // O usar shrinkWrap ListView.builder( shrinkWrap: true, ... )

❌ La lista no se actualiza cuando cambian los datos

Solución: Asegúrate de llamar setState() cuando modifiques la lista

void _addItem() { setState(() { items.add(newItem); }); }

❌ Scroll lento con items complejos

Solución: Optimiza el itemBuilder y usa const widgets

// Usar const widgets itemBuilder: (context, index) { return const MyListItemWidget(item: items[index]); } // Dividir widgets complejos itemBuilder: (context, index) { return ComplexItemWidget(item: items[index]); }

Recursos Adicionales

📚 Documentación Oficial

Consulta la documentación completa de ListView.Builder

Ver Documentación

🎥 Video Tutorial

Aprende con ejemplos prácticos de ListView.Builder

Ver Tutorial

💡 Ejemplos Avanzados

Descubre implementaciones complejas y optimizaciones

Explorar Ejemplos