
🎯欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
📜 本文将带你深入掌握 Flutter 中最常用的滚动列表组件------ListView,从基础用法到高级技巧,全面了解列表展示的各种方式。
一、ListView 组件概述
在移动应用开发中,列表是最常见的 UI 组件之一。无论是联系人列表、新闻列表还是商品列表,都需要使用滚动列表来展示数据。Flutter 的 ListView 组件提供了强大而灵活的列表展示功能,支持垂直和水平滚动,内置了滚动优化和性能优化机制。
🎯 为什么 ListView 如此重要?
ListView 是 Flutter 中最常用的滚动容器,它的重要性体现在以下几个方面:
- 内置滚动优化:自动处理滚动事件,支持惯性滚动和回弹效果
- 懒加载机制:只渲染可见区域的子组件,大幅提升性能
- 丰富的构造方式:提供多种构造函数,适应不同的使用场景
- 灵活的布局能力:支持垂直和水平滚动,支持自定义滚动控制器
- 高性能表现:通过 ItemExtent 等属性优化滚动性能
💡 核心思想:ListView 的核心优势在于其懒加载机制,它只创建和渲染当前可见的子组件,当用户滚动时,会回收不可见的子组件并创建新的可见组件。这种机制使得 ListView 可以高效地展示成千上万条数据,而不会出现性能问题。
二、ListView 的构造方式
2.1 ListView 默认构造函数
最简单的 ListView 使用方式是通过默认构造函数,传入一个子组件列表。
dart
ListView(
children: const [
ListTile(title: Text('项目 1')),
ListTile(title: Text('项目 2')),
ListTile(title: Text('项目 3')),
ListTile(title: Text('项目 4')),
ListTile(title: Text('项目 5')),
],
)
这种方式适合数据量较少(通常少于 20 个)的场景,因为所有子组件会被一次性创建。
⚠️ 注意:对于大量数据,不要使用默认构造函数,因为它会创建所有子组件,导致内存占用过高和性能问题。
2.2 ListView.builder 构造函数
ListView.builder 是最常用的构造函数,它通过 itemBuilder 回调按需创建子组件,适合展示大量数据。
dart
ListView.builder(
itemCount: 100, // 列表项数量
itemBuilder: (context, index) {
return ListTile(
title: Text('项目 ${index + 1}'),
);
},
)
ListView.builder 的参数说明:
| 参数 | 类型 | 说明 | 是否必填 |
|---|---|---|---|
itemCount |
int? | 列表项数量,null 表示无限列表 | 否 |
itemBuilder |
Widget Function | 列表项构建器 | 是 |
scrollDirection |
Axis | 滚动方向(vertical/horizontal) | 否 |
reverse |
bool | 是否反向滚动 | 否 |
padding |
EdgeInsets? | 内边距 | 否 |
itemExtent |
double? | 固定高度/宽度 | 否 |
2.3 ListView.separated 构造函数
ListView.separated 与 ListView.builder 类似,但可以在列表项之间添加分隔符。
dart
ListView.separated(
itemCount: 10,
separatorBuilder: (context, index) => const Divider(),
itemBuilder: (context, index) {
return ListTile(
title: Text('项目 ${index + 1}'),
);
},
)
这种方式非常适合需要分隔符的列表,如联系人列表、设置选项等。
2.4 ListView.custom 构造函数
ListView.custom 提供了最底层的自定义能力,可以通过自定义 SliverChildDelegate 来控制列表的渲染行为。
dart
ListView.custom(
childrenDelegate: SliverChildListDelegate(
const [
ListTile(title: Text('项目 1')),
ListTile(title: Text('项目 2')),
ListTile(title: Text('项目 3')),
],
),
)
这种方式适用于需要完全自定义列表渲染逻辑的高级场景。
三、ListView 的常用属性
3.1 滚动方向 scrollDirection
scrollDirection 控制列表的滚动方向,可以是垂直(默认)或水平。
dart
// 垂直滚动(默认)
ListView(
scrollDirection: Axis.vertical,
children: const [
Text('垂直项目 1'),
Text('垂直项目 2'),
],
)
// 水平滚动
ListView(
scrollDirection: Axis.horizontal,
children: const [
Text('水平项目 1'),
Text('水平项目 2'),
],
)
| 方向 | 值 | 适用场景 |
|---|---|---|
| 垂直 | Axis.vertical |
常见的列表、设置页面 |
| 水平 | Axis.horizontal |
图片轮播、标签列表 |
3.2 反向滚动 reverse
reverse 属性可以让列表从底部或右侧开始滚动。
dart
ListView(
reverse: true, // 从底部开始
children: const [
Text('项目 1'),
Text('项目 2'),
Text('项目 3'),
],
)
💡 小贴士:反向滚动常用于聊天应用,让最新消息显示在底部。
3.3 内边距 padding
padding 属性可以为列表添加内边距,避免内容紧贴屏幕边缘。
dart
ListView(
padding: const EdgeInsets.all(16),
children: const [
Text('有内边距的项目 1'),
Text('有内边距的项目 2'),
],
)
3.4 固定高度 itemExtent
itemExtent 可以指定每个列表项的固定高度,这可以显著提升滚动性能,因为 ListView 不需要测量每个子组件的高度。
dart
ListView.builder(
itemExtent: 56, // 每个列表项固定高度为 56
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(
title: Text('项目 ${index + 1}'),
);
},
)
⚠️ 注意 :只有当所有列表项的高度相同时才使用
itemExtent,否则会导致布局错误。
3.5 物理效果 physics
physics 属性控制列表的滚动物理效果,如是否回弹、是否允许滚动等。
dart
ListView(
physics: const BouncingScrollPhysics(), // iOS 风格回弹
children: const [
Text('项目 1'),
],
)
常用的 ScrollPhysics:
| 类 | 说明 | 适用场景 |
|---|---|---|
BouncingScrollPhysics |
iOS 风格,超出边界会回弹 | iOS 应用 |
ClampingScrollPhysics |
Android 风格,超出边界有发光效果 | Android 应用 |
NeverScrollableScrollPhysics |
禁止滚动 | 静态列表 |
AlwaysScrollableScrollPhysics |
始终可滚动,即使内容不足 | 需要下拉刷新 |
四、ScrollController 滚动控制
4.1 基础用法
ScrollController 可以用来控制 ListView 的滚动位置,监听滚动事件。
dart
class MyListView extends StatefulWidget {
const MyListView({super.key});
@override
State<MyListView> createState() => _MyListViewState();
}
class _MyListViewState extends State<MyListView> {
final ScrollController _controller = ScrollController();
@override
void dispose() {
_controller.dispose(); // 释放控制器
super.dispose();
}
void _scrollToTop() {
_controller.animateTo(
0,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
}
@override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(
onPressed: _scrollToTop,
child: const Text('回到顶部'),
),
Expanded(
child: ListView.builder(
controller: _controller,
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(
title: Text('项目 ${index + 1}'),
);
},
),
),
],
);
}
}
4.2 监听滚动事件
通过监听 ScrollController 的变化,可以实现滚动位置检测、滚动到底部加载更多等功能。
dart
class ScrollListenerExample extends StatefulWidget {
const ScrollListenerExample({super.key});
@override
State<ScrollListenerExample> createState() => _ScrollListenerExampleState();
}
class _ScrollListenerExampleState extends State<ScrollListenerExample> {
final ScrollController _controller = ScrollController();
bool _isAtTop = true;
@override
void initState() {
super.initState();
_controller.addListener(_scrollListener);
}
void _scrollListener() {
if (_controller.offset <= _controller.position.minScrollExtent &&
!_controller.position.outOfRange) {
setState(() {
_isAtTop = true;
});
} else if (_controller.offset >= _controller.position.maxScrollExtent &&
!_controller.position.outOfRange) {
// 滚动到底部,可以加载更多
_loadMore();
} else {
setState(() {
_isAtTop = false;
});
}
}
void _loadMore() {
// 加载更多数据的逻辑
}
@override
void dispose() {
_controller.removeListener(_scrollListener);
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('滚动监听示例'),
),
floatingActionButton: _isAtTop
? null
: FloatingActionButton(
onPressed: () {
_controller.animateTo(0,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut);
},
child: const Icon(Icons.arrow_upward),
),
body: ListView.builder(
controller: _controller,
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(
title: Text('项目 ${index + 1}'),
);
},
),
);
}
}
五、下拉刷新与上拉加载
5.1 RefreshIndicator 下拉刷新
RefreshIndicator 是 Flutter 提供的下拉刷新组件,Material Design 风格的下拉刷新效果。
dart
class RefreshExample extends StatefulWidget {
const RefreshExample({super.key});
@override
State<RefreshExample> createState() => _RefreshExampleState();
}
class _RefreshExampleState extends State<RefreshExample> {
final List<String> _items = List.generate(20, (index) => '项目 ${index + 1}');
Future<void> _refresh() async {
await Future.delayed(const Duration(seconds: 1));
setState(() {
_items.insert(0, '新项目 ${DateTime.now().millisecondsSinceEpoch}');
});
}
@override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: _refresh,
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_items[index]),
);
},
),
);
}
}
5.2 上拉加载更多
通过监听滚动到底部事件,实现上拉加载更多功能。
dart
class LoadMoreExample extends StatefulWidget {
const LoadMoreExample({super.key});
@override
State<LoadMoreExample> createState() => _LoadMoreExampleState();
}
class _LoadMoreExampleState extends State<LoadMoreExample> {
final ScrollController _controller = ScrollController();
final List<String> _items = List.generate(20, (index) => '项目 ${index + 1}');
bool _isLoading = false;
@override
void initState() {
super.initState();
_controller.addListener(_scrollListener);
}
void _scrollListener() {
if (_controller.position.pixels == _controller.position.maxScrollExtent) {
_loadMore();
}
}
Future<void> _loadMore() async {
if (_isLoading) return;
setState(() {
_isLoading = true;
});
await Future.delayed(const Duration(seconds: 1));
setState(() {
_items.addAll(List.generate(10, (index) => '项目 ${_items.length + index + 1}'));
_isLoading = false;
});
}
@override
void dispose() {
_controller.removeListener(_scrollListener);
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView.builder(
controller: _controller,
itemCount: _items.length + (_isLoading ? 1 : 0),
itemBuilder: (context, index) {
if (index == _items.length) {
return const Padding(
padding: EdgeInsets.all(16),
child: Center(child: CircularProgressIndicator()),
);
}
return ListTile(
title: Text(_items[index]),
);
},
);
}
}
六、常用列表项组件
6.1 ListTile
ListTile 是 Material Design 的标准列表项,支持标题、副标题、图标等多种元素。
dart
ListTile(
leading: const CircleAvatar(
backgroundImage: NetworkImage('https://picsum.photos/100/100'),
),
title: const Text('用户名称'),
subtitle: const Text('用户简介信息'),
trailing: const Icon(Icons.chevron_right),
onTap: () {
// 点击事件
},
)
ListTile 的主要属性:
| 属性 | 类型 | 说明 |
|---|---|---|
leading |
Widget? | 前置图标 |
title |
Widget? | 标题 |
subtitle |
Widget? | 副标题 |
trailing |
Widget? | 后置图标 |
onTap |
VoidCallback? | 点击事件 |
6.2 Card
Card 可以为列表项添加卡片效果,提升视觉层次。
dart
Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: ListTile(
title: const Text('卡片列表项'),
subtitle: const Text('这是卡片样式的列表项'),
),
)
6.3 自定义列表项
除了使用标准组件,也可以完全自定义列表项的样式。
dart
Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Colors.blue, Colors.purple],
),
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'自定义列表项',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
SizedBox(height: 8),
Text(
'这是完全自定义样式的列表项',
style: TextStyle(
fontSize: 14,
color: Colors.white70,
),
),
],
),
)
七、性能优化
7.1 使用 ListView.builder
对于大量数据,务必使用 ListView.builder 而不是默认构造函数。
dart
// ❌ 性能差:创建所有子组件
ListView(
children: List.generate(1000, (index) => ListTile(title: Text('项目 $index'))),
)
// ✅ 性能好:按需创建子组件
ListView.builder(
itemCount: 1000,
itemBuilder: (context, index) => ListTile(title: Text('项目 $index')),
)
7.2 使用 itemExtent
如果所有列表项的高度相同,使用 itemExtent 可以显著提升性能。
dart
ListView.builder(
itemExtent: 56, // 固定高度
itemCount: 1000,
itemBuilder: (context, index) => ListTile(title: Text('项目 $index')),
)
7.3 避免在 itemBuilder 中创建复杂对象
将复杂对象的创建移到 itemBuilder 外部,避免重复创建。
dart
class OptimizedListView extends StatelessWidget {
final List<String> _titles = List.generate(100, (index) => '标题 $index');
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: _titles.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_titles[index]), // 直接使用预先创建的数据
);
},
);
}
}
7.4 使用 const 构造函数
对于不变的子组件,使用 const 构造函数。
dart
ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return const ListTile(
leading: Icon(Icons.star),
title: Text('固定内容'),
);
},
)
八、实际应用场景
8.1 联系人列表
dart
ListView.builder(
itemCount: contacts.length,
itemBuilder: (context, index) {
final contact = contacts[index];
return ListTile(
leading: CircleAvatar(
child: Text(contact.name[0]),
),
title: Text(contact.name),
subtitle: Text(contact.phone),
onTap: () {
// 打开联系人详情
},
);
},
)
8.2 新闻列表
dart
ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: news.length,
itemBuilder: (context, index) {
final item = news[index];
return Card(
margin: const EdgeInsets.only(bottom: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(item.imageUrl),
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
item.summary,
style: TextStyle(color: Colors.grey[600]),
),
],
),
),
],
),
);
},
)
8.3 设置页面
dart
ListView.separated(
itemCount: settings.length,
separatorBuilder: (context, index) => const Divider(),
itemBuilder: (context, index) {
final setting = settings[index];
return ListTile(
leading: Icon(setting.icon),
title: Text(setting.title),
trailing: const Icon(Icons.chevron_right),
onTap: () {
// 打开设置页面
},
);
},
)
九、完整示例代码
下面是一个完整的 Flutter 应用示例,展示 ListView 组件的各种用法。
dart
import 'package:flutter/material.dart';
void main() {
runApp(const ListViewDemo());
}
class ListViewDemo extends StatelessWidget {
const ListViewDemo({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ListView 组件演示',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.dark(
primary: const Color(0xFF6366F1),
secondary: const Color(0xFF8B5CF6),
surface: const Color(0xFF1E293B),
background: const Color(0xFF0F172A),
brightness: Brightness.dark,
),
useMaterial3: true,
),
home: const ListViewPage(),
);
}
}
class ListViewPage extends StatelessWidget {
const ListViewPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF0F172A),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题区域
Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
const Color(0xFF6366F1).withOpacity(0.2),
const Color(0xFF8B5CF6).withOpacity(0.2),
],
),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: Colors.white.withOpacity(0.1),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'📜 ListView',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.white,
letterSpacing: 0.5,
),
),
const SizedBox(height: 8),
Text(
'探索 Flutter 中滚动列表的各种用法',
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(0.7),
height: 1.5,
),
),
],
),
),
const SizedBox(height: 32),
// 基础列表
_buildSection(
title: '基础列表',
icon: Icons.list,
color: Colors.blue,
child: _buildListCard([
SizedBox(
height: 200,
child: ListView(
children: const [
ListTile(
leading: Icon(Icons.star, color: Colors.blue),
title: Text('基础列表项 1'),
),
ListTile(
leading: Icon(Icons.star, color: Colors.blue),
title: Text('基础列表项 2'),
),
ListTile(
leading: Icon(Icons.star, color: Colors.blue),
title: Text('基础列表项 3'),
),
ListTile(
leading: Icon(Icons.star, color: Colors.blue),
title: Text('基础列表项 4'),
),
ListTile(
leading: Icon(Icons.star, color: Colors.blue),
title: Text('基础列表项 5'),
),
],
),
),
]),
),
const SizedBox(height: 24),
// Builder 列表
_buildSection(
title: 'ListView.builder',
icon: Icons.build,
color: Colors.green,
child: _buildListCard([
SizedBox(
height: 200,
child: ListView.builder(
itemCount: 20,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.green.withOpacity(0.3),
child: Text(
'${index + 1}',
style: const TextStyle(color: Colors.white),
),
),
title: Text('动态生成项 ${index + 1}'),
subtitle: Text('这是第 ${index + 1} 个列表项'),
);
},
),
),
]),
),
const SizedBox(height: 24),
// Separated 列表
_buildSection(
title: 'ListView.separated',
icon: Icons.horizontal_rule,
color: Colors.purple,
child: _buildListCard([
SizedBox(
height: 200,
child: ListView.separated(
itemCount: 5,
separatorBuilder: (context, index) => Divider(
color: Colors.white.withOpacity(0.1),
),
itemBuilder: (context, index) {
return ListTile(
title: Text('带分隔符的项 ${index + 1}'),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
);
},
),
),
]),
),
const SizedBox(height: 24),
// 水平滚动
_buildSection(
title: '水平滚动',
icon: Icons.swap_horiz,
color: Colors.orange,
child: _buildListCard([
SizedBox(
height: 120,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 10,
itemBuilder: (context, index) {
return Container(
width: 100,
margin: const EdgeInsets.only(right: 12),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.orange.withOpacity(0.3),
Colors.red.withOpacity(0.3),
],
),
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: Text(
'项 ${index + 1}',
style: const TextStyle(color: Colors.white),
),
),
);
},
),
),
]),
),
const SizedBox(height: 24),
// 卡片列表
_buildSection(
title: '卡片列表',
icon: Icons.style,
color: Colors.pink,
child: _buildListCard([
SizedBox(
height: 250,
child: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 8),
itemCount: 5,
itemBuilder: (context, index) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CircleAvatar(
backgroundColor: Colors.pink.withOpacity(0.3),
child: Icon(
Icons.person,
color: Colors.pink,
size: 20,
),
),
const SizedBox(width: 12),
const Expanded(
child: Text(
'卡片标题',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 8),
Text(
'这是第 ${index + 1} 张卡片的内容描述,展示了卡片列表的使用方式。',
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(0.7),
),
),
],
),
),
);
},
),
),
]),
),
const SizedBox(height: 24),
// 实际应用
_buildSection(
title: '实际应用示例',
icon: Icons.apps,
color: Colors.cyan,
child: _buildListCard([
const ContactListExample(),
const SizedBox(height: 16),
const SettingsListExample(),
]),
),
const SizedBox(height: 80),
],
),
),
),
);
}
Widget _buildSection({
required String title,
required IconData icon,
required Color color,
required Widget child,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.2),
borderRadius: BorderRadius.circular(10),
),
child: Icon(icon, color: color, size: 20),
),
const SizedBox(width: 12),
Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
],
),
const SizedBox(height: 12),
child,
],
);
}
Widget _buildListCard(List<Widget> children) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.03),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: Colors.white.withOpacity(0.05),
),
),
child: Column(
children: children,
),
);
}
}
class ContactListExample extends StatelessWidget {
const ContactListExample({super.key});
final List<Map<String, dynamic>> _contacts = const [
{'name': '张三', 'phone': '138-0000-0001'},
{'name': '李四', 'phone': '138-0000-0002'},
{'name': '王五', 'phone': '138-0000-0003'},
{'name': '赵六', 'phone': '138-0000-0004'},
];
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'联系人列表',
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(0.7),
),
),
const SizedBox(height: 12),
SizedBox(
height: 200,
child: ListView.builder(
itemCount: _contacts.length,
itemBuilder: (context, index) {
final contact = _contacts[index];
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.cyan.withOpacity(0.3),
child: Text(
contact['name'][0],
style: const TextStyle(color: Colors.white),
),
),
title: Text(contact['name']),
subtitle: Text(contact['phone']),
trailing: const Icon(Icons.call, color: Colors.cyan),
);
},
),
),
],
);
}
}
class SettingsListExample extends StatelessWidget {
const SettingsListExample({super.key});
final List<Map<String, dynamic>> _settings = const [
{'icon': Icons.wifi, 'title': 'Wi-Fi', 'subtitle': '已连接'},
{'icon': Icons.bluetooth, 'title': '蓝牙', 'subtitle': '已开启'},
{'icon': Icons.notifications, 'title': '通知', 'subtitle': '已允许'},
{'icon': Icons.lock, 'title': '隐私', 'subtitle': '管理权限'},
];
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'设置列表',
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(0.7),
),
),
const SizedBox(height: 12),
SizedBox(
height: 200,
child: ListView.separated(
itemCount: _settings.length,
separatorBuilder: (context, index) => Divider(
color: Colors.white.withOpacity(0.1),
height: 1,
),
itemBuilder: (context, index) {
final setting = _settings[index];
return ListTile(
leading: Icon(
setting['icon'],
color: Colors.cyan,
),
title: Text(setting['title']),
subtitle: Text(setting['subtitle']),
trailing: const Icon(Icons.chevron_right, size: 20),
);
},
),
),
],
);
}
}
十、总结
ListView 是 Flutter 中最常用的滚动容器组件,掌握它的各种用法对于构建数据密集型应用至关重要。
🎯 核心要点
- 构造方式:ListView(默认)、builder(大量数据)、separated(带分隔符)、custom(自定义)
- 懒加载机制:只渲染可见区域,性能优异
- ScrollController:控制滚动位置,监听滚动事件
- 下拉刷新:使用 RefreshIndicator 实现
- 上拉加载:监听滚动到底部事件实现
- 性能优化:使用 builder、itemExtent、避免重复创建对象
📚 使用建议
| 场景 | 推荐方案 |
|---|---|
| 少量数据(<20) | ListView 默认构造函数 |
| 大量数据 | ListView.builder |
| 需要分隔符 | ListView.separated |
| 联系人列表 | ListTile + CircleAvatar |
| 新闻列表 | Card + 图片 + 文字 |
| 设置页面 | ListTile.separated |
| 图片轮播 | ListView(水平滚动) |
| 下拉刷新 | RefreshIndicator |
💡 最佳实践:对于任何可能包含大量数据的列表,都应使用 ListView.builder 而不是默认构造函数。合理使用 ScrollController 可以实现更多交互效果,如回到顶部、滚动监听等。通过合理的设计和优化,ListView 可以高效地展示成千上万条数据,为用户提供流畅的滚动体验。