借助 ListView.builder、ListTile 以及被提取为独立 widget 的列表项,流畅地显示成千上万条数据而不卡顿。
在 Kodokon 中打开本课真实的应用几乎总要显示列表:消息、商品、联系人。你已经认识的 ListView(children: ...) 构造函数会一次性构建它的所有子项 - 十来个项目没问题,上千个就是灾难。ListView.builder 只在滚动时按需构建可见区域附近的项目。
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]),
);
},
),
);
}
}两个参数撑起了整套机制:itemCount 声明项目的总数,itemBuilder 则在某个索引接近屏幕时才生成对应的项目。对于每一行的内容,ListTile 是你在 Material 中的好帮手:leading、title、subtitle 和 trailing 负责组织这一行的结构,而 onTap 会在触摸时提供墨水波纹效果。但一旦某个项目变得复杂,就别让它在 itemBuilder 里膨胀:把它提取成独立的 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,
);
}
}被提取出来的项目可以单独测试、在其他界面中复用,并让 itemBuilder 变得非常简单。还剩一个常见需求:分隔线。ListView.separated 的用法和 builder 一样,只是多了第三个参数 separatorBuilder,它会在每两个项目之间被调用 - 最后一个元素后面永远不会跟着分隔线。
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]),
);
},
);
}
}