Zeige Listen mit Tausenden von Einträgen ohne Ruckler an, dank ListView.builder, ListTile und in eigene Widgets ausgelagerten Einträgen.
Diese Lektion in Kodokon öffnenEine echte App zeigt fast immer Listen an: Nachrichten, Produkte, Kontakte. Der Konstruktor ListView(children: ...), den du schon kennst, baut alle seine Kinder auf einmal - in Ordnung bei zehn Einträgen, katastrophal bei tausend. ListView.builder baut nur die Einträge in der Nähe des sichtbaren Bereichs, bei Bedarf, während du scrollst.
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]),
);
},
),
);
}
}Zwei Parameter tragen den gesamten Mechanismus: itemCount gibt die Gesamtzahl der Einträge an, und itemBuilder erzeugt den Eintrag an einem bestimmten Index, erst wenn er sich dem Bildschirm nähert. Für den Inhalt jeder Zeile ist ListTile dein Material-Verbündeter: leading, title, subtitle und trailing strukturieren die Zeile, und onTap liefert den Ink-Ripple bei Berührung. Sobald ein Eintrag aber wächst, lass ihn nicht innerhalb von itemBuilder aufblähen: Lagere ihn in ein eigenes Widget aus.
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,
);
}
}Ein ausgelagerter Eintrag lässt sich isoliert testen, auf anderen Screens wiederverwenden und macht den itemBuilder trivial. Ein häufiges Bedürfnis bleibt: Trenner. ListView.separated funktioniert wie builder, mit einem dritten Parameter, separatorBuilder, der zwischen jedem Paar von Einträgen aufgerufen wird - dem letzten Element folgt nie ein Trenner.
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]),
);
},
);
}
}