Muestra listas de miles de elementos sin ralentizaciones gracias a ListView.builder, ListTile y elementos extraídos en sus propios widgets.
Abrir esta lección en KodokonUna 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.
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]),
);
},
),
);
}
}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.
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 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.
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]),
);
},
);
}
}