Kodokon kodokon.com

Listas de alto rendimiento con ListView.builder

Muestra listas de miles de elementos sin ralentizaciones gracias a ListView.builder, ListTile y elementos extraídos en sus propios widgets.

8 min · 3 preguntas

Abrir esta lección en Kodokon

Una aplicación real casi siempre muestra listas: mensajes, productos, contactos. El constructor ListView(children: ...) que ya conoces construye todos sus hijos de una sola vez: bien para diez elementos, desastroso para mil. ListView.builder solo construye los elementos cercanos al área visible, bajo demanda, a medida que te desplazas.

DART
import 'package:flutter/material.dart';

class ContactsScreen extends StatelessWidget {
  const ContactsScreen({super.key, required this.names});

  final List<String> names;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Contacts')),
      body: ListView.builder(
        itemCount: names.length,
        itemBuilder: (context, index) {
          return ListTile(
            leading: const Icon(Icons.person),
            title: Text(names[index]),
          );
        },
      ),
    );
  }
}
Solo se construyen los elementos visibles, incluso con 10 000 nombres.

Dos parámetros sostienen todo el mecanismo: itemCount declara el número total de elementos, e itemBuilder produce el elemento en un índice dado, solo cuando se acerca a la pantalla. Para el contenido de cada fila, ListTile es tu aliado de Material: leading, title, subtitle y trailing estructuran la fila, y onTap proporciona el efecto de onda al tocar. Pero en cuanto un elemento crece, no dejes que se infle dentro de itemBuilder: extráelo en su propio widget.

DART
import 'package:flutter/material.dart';

class ContactTile extends StatelessWidget {
  const ContactTile({
    super.key,
    required this.name,
    required this.onTap,
  });

  final String name;
  final VoidCallback onTap;

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: CircleAvatar(child: Text(name[0])),
      title: Text(name),
      trailing: const Icon(Icons.chevron_right),
      onTap: onTap,
    );
  }
}
Un elemento de lista convertido en un widget reutilizable y comprobable.

Un elemento extraído se puede probar de forma aislada, reutilizar en otras pantallas y vuelve trivial el itemBuilder. Queda una necesidad frecuente: los separadores. ListView.separated funciona como builder con un tercer parámetro, separatorBuilder, invocado entre cada par de elementos: el último elemento nunca va seguido de un separador.

DART
import 'package:flutter/material.dart';

class ContactList extends StatelessWidget {
  const ContactList({super.key, required this.names});

  final List<String> names;

  @override
  Widget build(BuildContext context) {
    return ListView.separated(
      itemCount: names.length,
      separatorBuilder: (context, index) {
        return const Divider(height: 1);
      },
      itemBuilder: (context, index) {
        return ContactTile(
          name: names[index],
          onTap: () => debugPrint(names[index]),
        );
      },
    );
  }
}
separatorBuilder inserta un Divider entre cada par de elementos.

Prueba de conocimientos

Comprueba que has retenido los puntos clave de esta lección.

  1. ¿Por qué preferir ListView.builder frente a ListView(children: ...) para una lista de 500 elementos?
    • Ordena los elementos automáticamente
    • Solo construye los elementos visibles, bajo demanda
    • Almacena las imágenes en caché
    • Añade separadores automáticamente
  2. ¿Qué constructor inserta un widget entre cada par de elementos?
    • ListView.divided
    • ListView.builder
    • ListView.separated
  3. ¿Cuál es la ventaja de extraer un elemento de lista en su propia clase de widget?
    • Es obligatorio para que ListView.builder compile
    • Habilita const, aísla las reconstrucciones y facilita la reutilización
    • Hace que la lista se pueda desplazar
    • Te ahorra escribir itemCount