这节课是 Flutter 开发高频实战核心APP 中 80% 的页面都包含滚动列表如商品列表、消息列表、相册、我的订单我们会系统学习 Flutter 两大滚动列表组件ListView线性列表纵向 / 横向、GridView网格列表重点掌握懒加载构建适配海量数据、列表项交互、下拉刷新RefreshIndicator、上拉加载更多最终实现企业级标准的可刷新、可加载、高性能的列表页面适配绝大多数业务场景。课前回顾组件封装抽离通用 UI一行代码复用提升开发效率跨组件传值InheritedWidget 实现全局轻量状态共享告别层层传参生命周期规范资源管理initState初始化、dispose销毁避免内存泄漏前置基础StatefulWidget列表需维护数据 / 加载状态、ListTile快速实现列表项。一、列表核心认知为什么用「懒加载」Flutter 中列表有直接构建和懒加载构建两种方式直接构建通过ListView(children: [...])直接传入所有列表项适合少量数据几十条内懒加载构建通过ListView.builder/GridView.builder根据屏幕可见范围动态创建列表项适合海量数据几百 / 几千条不会一次性创建所有组件大幅节省内存、提升性能。核心原则开发中无论数据多少优先使用懒加载构建builder方式这是 Flutter 列表开发的性能优化最佳实践避免因数据量过大导致页面卡顿、内存溢出。二、ListView线性列表核心纵向 / 横向ListView是 Flutter 最基础的滚动列表支持纵向默认和横向滚动核心使用ListView.builder懒加载构建配套ListTile快速实现列表项标题、副标题、图标、右侧箭头无需手动写布局。1. 基础懒加载列表ListView.builder ListTile适用于纯展示型列表如消息列表、设置列表核心属性itemCount数据长度、itemBuilder动态构建列表项结合StatefulWidget维护列表数据。实战代码简单消息列表dartimport package:flutter/material.dart; class MessageListPage extends StatefulWidget { const MessageListPage({super.key}); override StateMessageListPage createState() _MessageListPageState(); } class _MessageListPageState extends StateMessageListPage { // 列表数据模拟100条消息海量数据懒加载适配 late ListString _messageList; // 生命周期初始化列表数据 override void initState() { super.initState(); _initData(); } // 初始化模拟数据 void _initData() { _messageList []; for (int i 1; i 100; i) { _messageList.add(这是第$i条消息Flutter列表实战); } } // 构建列表项 Widget _buildItem(int index) { String content _messageList[index]; return ListTile( leading: const Icon(Icons.message, color: Colors.blue, size: 24), // 左侧图标 title: Text(content, style: const TextStyle(fontSize: 16)), // 主标题 subtitle: const Text(2026-02-06 18:00, style: TextStyle(fontSize: 12, color: Colors.grey)), // 副标题 trailing: const Icon(Icons.arrow_forward_ios, size: 16, color: Colors.grey), // 右侧箭头 onTap: () { // 列表项点击事件 ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(点击了$content)), ); }, // 列表项分割线替代全局Divider更灵活 dense: true, // 紧凑布局减少高度 ); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text(消息列表ListView), centerTitle: true, ), body: // 懒加载列表核心 ListView.builder( itemCount: _messageList.length, // 数据长度必传 itemBuilder: (context, index) _buildItem(index), // 动态构建列表项 itemExtent: 70, // 固定列表项高度可选提升性能 padding: const EdgeInsets.symmetric(vertical: 8), // 列表内边距 separatorBuilder: (context, index) const Divider( // 列表项之间的分割线 height: 1, color: Color(0xFFF0F0F0), indent: 16, endIndent: 16, ), ), ); } }核心属性说明表格属性作用实战价值itemCount列表项数量必传决定列表长度懒加载的核心依据itemBuilder动态构建列表项参数为(context, index)懒加载核心仅创建可见区域的列表项itemExtent固定列表项高度避免 Flutter 计算列表项高度大幅提升滚动性能推荐设置separatorBuilder列表项之间的分割线替代手动在列表项中加 Divider布局更整洁padding列表的内边距控制列表与屏幕的间距提升视觉体验physics滚动物理效果AlwaysScrollableScrollPhysics()始终可滚、ClampingScrollPhysics()安卓默认、BouncingScrollPhysics()iOS 弹性2. 横向 ListView横向滚动列表只需给ListView.builder添加scrollDirection: Axis.horizontal即可实现横向滚动列表如商品导航、分类标签配合itemExtent固定宽度。实战代码横向分类列表dartWidget _buildHorizontalList() { // 分类数据 ListString categories [推荐, 热点, 科技, 娱乐, 体育, 财经, 汽车, 美食, 旅游]; return SizedBox( height: 60, // 固定横向列表高度必传 child: ListView.builder( scrollDirection: Axis.horizontal, // 横向滚动 itemCount: categories.length, itemExtent: 80, // 固定列表项宽度 padding: const EdgeInsets.symmetric(horizontal: 16), itemBuilder: (context, index) { String title categories[index]; return Center( child: Text( title, style: TextStyle( fontSize: 16, color: index 0 ? Colors.blue : Colors.black87, // 第一个项高亮 fontWeight: index 0 ? FontWeight.bold : FontWeight.normal, ), ), ); }, ), ); }注意横向 ListView必须包裹在 SizedBox/Container 中并指定高度否则会因高度无限大导致布局报错。3. 自定义列表项替代 ListTileListTile适合快速开发实际项目中列表项样式更复杂如多图片、多文本、自定义布局需通过Row/Column/Container自定义列表项保持懒加载逻辑不变。实战代码自定义商品列表项dart// 自定义商品列表项构建方法 Widget _buildGoodsItem(int index) { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 商品图片 Container( width: 80, height: 80, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), image: DecorationImage( image: NetworkImage(https://picsum.photos/200/200?random$index), // 随机图片index保证唯一 fit: BoxFit.cover, ), ), ), const SizedBox(width: 12), // 商品信息 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( Flutter实战商品$index 高品质精选好物 性价比超高, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500), maxLines: 2, // 最多2行 overflow: TextOverflow.ellipsis, // 超出省略 ), const SizedBox(height: 8), Text( ¥${(index 1) * 10}.00, style: const TextStyle(fontSize: 18, color: Colors.red, fontWeight: FontWeight.bold), ), const SizedBox(height: 4), Text( 已售${(index 1) * 100}, style: const TextStyle(fontSize: 12, color: Colors.grey), ), ], ), ), ], ), ); }技巧自定义列表项时网络图片使用index作为随机参数保证每个列表项图片不同文本添加maxLinesoverflow避免文本溢出导致布局混乱。三、GridView网格列表核心商品 / 相册GridView用于实现网格状滚动列表如商品列表、相册、九宫格核心同样使用懒加载构建GridView.builder关键属性是gridDelegate网格布局委托控制列数、间距、宽高比。核心布局委托SliverGridDelegateWithFixedCrossAxisCount开发中最常用的网格委托固定列数如 2 列、3 列适配所有网格场景核心参数crossAxisCount列数必传如 2 两列、3 三列mainAxisSpacing纵向间距行与行之间crossAxisSpacing横向间距列与列之间childAspectRatio子组件宽高比如 1 正方形、1.5 宽是高的 1.5 倍。实战代码两列商品网格列表企业级标准dartimport package:flutter/material.dart; class GoodsGridViewPage extends StatefulWidget { const GoodsGridViewPage({super.key}); override StateGoodsGridViewPage createState() _GoodsGridViewPageState(); } class _GoodsGridViewPageState extends StateGoodsGridViewPage { late ListString _goodsList; override void initState() { super.initState(); _initGoodsData(); } // 初始化20条商品数据 void _initGoodsData() { _goodsList []; for (int i 1; i 20; i) { _goodsList.add(商品$i); } } // 构建网格列表项 Widget _buildGoodsItem(int index) { String goodsName _goodsList[index]; return GestureDetector( onTap: () { // 网格项点击事件 ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(点击了$goodsName))); }, child: Container( margin: const EdgeInsets.all(4), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: Colors.white, boxShadow: [ // 轻微阴影提升质感 BoxShadow(color: Colors.grey.withOpacity(0.1), blurRadius: 4, spreadRadius: 2), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 商品图片占满宽度按比例高度 Expanded( child: Container( width: double.infinity, decoration: BoxDecoration( borderRadius: const BorderRadius.vertical(top: Radius.circular(10)), image: DecorationImage( image: NetworkImage(https://picsum.photos/200/200?random$index), fit: BoxFit.cover, ), ), ), ), // 商品信息 Padding( padding: const EdgeInsets.all(8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( goodsName 高品质精选好物 性价比超高, maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), ), const SizedBox(height: 4), Text( ¥${(index 1) * 20}.00, style: const TextStyle(fontSize: 16, color: Colors.red, fontWeight: FontWeight.bold), ), ], ), ), ], ), ), ); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text(商品网格列表GridView), centerTitle: true, backgroundColor: Colors.white, elevation: 1, ), backgroundColor: const Color(0xFFF8F8F8), // 页面背景色 body: GridView.builder( itemCount: _goodsList.length, itemBuilder: (context, index) _buildGoodsItem(index), // 网格布局委托固定2列 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, // 两列 mainAxisSpacing: 8, // 纵向间距 crossAxisSpacing: 8, // 横向间距 childAspectRatio: 0.85, // 宽高比宽0.85高1商品图竖版 ), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), ), ); } }GridView 核心注意事项背景色网格列表页面建议设置浅灰色背景#F8F8F8网格项设置白色背景 轻微阴影提升视觉层次感宽高比childAspectRatio根据业务调整商品列表常用0.85竖版图、相册常用1正方形点击事件网格项无内置onTap需通过GestureDetector包裹实现点击性能优化网格项中图片使用Expandedwidth: double.infinity占满宽度避免图片变形文本添加maxLinesoverflow。四、下拉刷新 上拉加载列表实战必备功能APP 中的列表几乎都包含下拉刷新刷新最新数据和上拉加载加载更多数据Flutter 提供原生RefreshIndicator实现下拉刷新上拉加载通过滚动监听ScrollController实现两者结合是企业级列表的标准配置。核心实现思路下拉刷新用RefreshIndicator包裹列表实现onRefresh异步方法刷新时重新请求第一页数据重置列表上拉加载创建ScrollController监听滚动位置当滚动到列表底部时加载下一页数据状态维护添加加载状态isLoading、是否有更多数据hasMore、当前页码page避免重复加载、无数据时继续加载资源管理ScrollController需在initState初始化dispose销毁避免内存泄漏。实战代码带下拉刷新 上拉加载的高性能列表综合版dartimport package:flutter/material.dart; import dart:async; class RefreshLoadListPage extends StatefulWidget { const RefreshLoadListPage({super.key}); override StateRefreshLoadListPage createState() _RefreshLoadListPageState(); } class _RefreshLoadListPageState extends StateRefreshLoadListPage { late ListString _listData; // 列表数据 late ScrollController _scrollController; // 滚动控制器上拉加载核心 int _page 1; // 当前页码初始为1 bool _isLoading false; // 是否正在加载避免重复请求 bool _hasMore true; // 是否有更多数据 final int _pageSize 10; // 每页加载10条 // 生命周期初始化资源 override void initState() { super.initState(); _listData []; // 初始化滚动控制器 _scrollController ScrollController(); // 添加滚动监听 _scrollController.addListener(_scrollListener); // 初始化加载第一页数据 _loadData(isRefresh: true); } // 滚动监听判断是否到达底部触发上拉加载 void _scrollListener() { // 条件滚动到最底部 不是正在加载 有更多数据 if (_scrollController.position.pixels _scrollController.position.maxScrollExtent - 50 !_isLoading _hasMore) { _loadData(isRefresh: false); // 加载下一页 } } // 加载数据isRefreshtrue下拉刷新第一页false上拉加载下一页 Futurevoid _loadData({required bool isRefresh}) async { if (_isLoading) return; // 正在加载直接返回 setState(() { _isLoading true; // 标记为正在加载 if (isRefresh) _page 1; // 刷新则重置页码为1 }); // 模拟网络请求延迟1秒模拟异步请求 await Future.delayed(const Duration(seconds: 1), () { ListString newData []; int start (isRefresh ? 1 : (_page - 1) * _pageSize 1); int end start _pageSize - 1; // 模拟最多5页数据超过则无更多 if (end 50) { end 50; _hasMore false; } for (int i start; i end; i) { newData.add(这是第$i条数据 - ${isRefresh ? 下拉刷新 : 上拉加载}); } // 更新数据 if (mounted) { // 异步操作判断组件是否挂载 setState(() { if (isRefresh) { _listData newData; // 刷新重置数据 } else { _listData.addAll(newData); // 加载追加数据 } _page; // 页码1 _isLoading false; // 标记为加载完成 }); } }); } // 下拉刷新方法RefreshIndicator的回调 Futurevoid _onRefresh() async { _hasMore true; // 刷新时重置有更多数据 await _loadData(isRefresh: true); } // 构建列表项 Widget _buildItem(int index) { return ListTile( title: Text(_listData[index], style: const TextStyle(fontSize: 16)), leading: CircleAvatar(child: Text(${index 1})), onTap: () ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(点击了第${index 1}条数据)), ), ); } // 构建上拉加载底部提示 Widget _buildLoadMoreWidget() { if (!_hasMore) { return const Center( child: Padding( padding: EdgeInsets.symmetric(vertical: 16), child: Text(没有更多数据了, style: TextStyle(color: Colors.grey)), ), ); } else { return const Center( child: Padding( padding: EdgeInsets.symmetric(vertical: 16), child: CircularProgressIndicator(strokeWidth: 2), ), ); } } // 生命周期销毁资源 override void dispose() { super.dispose(); // 移除滚动监听 _scrollController.removeListener(_scrollListener); // 销毁滚动控制器 _scrollController.dispose(); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text(下拉刷新上拉加载), centerTitle: true, ), // 下拉刷新核心RefreshIndicator包裹列表 body: RefreshIndicator( color: Colors.blue, // 刷新指示器颜色 onRefresh: _onRefresh, // 下拉刷新回调必须是异步方法 child: ListView.builder( controller: _scrollController, // 绑定滚动控制器 itemCount: _listData.length 1, // 数据长度1底部加载提示 itemBuilder: (context, index) { if (index _listData.length) { return _buildItem(index); // 构建列表项 } else { return _buildLoadMoreWidget(); // 构建底部加载提示 } }, itemExtent: 60, // 固定高度提升性能 separatorBuilder: (context, index) const Divider(height: 1, color: Color(0xFFF0F0F0)), ), ), ); } }核心功能效果下拉刷新向下拉动列表出现蓝色刷新指示器松开后加载第一页数据重置列表上拉加载向上滚动到列表底部 50px 时自动加载下一页数据底部显示加载动画防重复加载_isLoading标记加载过程中不会触发新的请求无更多数据加载到 5 页后底部显示「没有更多数据了」不再触发上拉加载性能优化itemExtent固定高度ScrollController规范销毁异步请求判断mounted。关键知识点必背RefreshIndicator必须包裹可滚动组件ListView/GridViewonRefresh必须是Future 异步方法否则刷新指示器不会消失可通过color/backgroundColor自定义指示器样式。滚动监听maxScrollExtent列表最大滚动位置底部减 50 是提前加载距离底部 50px 时触发提升用户体验避免直接判断 maxScrollExtent因滚动精度问题可能无法触发。状态管理_page当前页码刷新重置为 1加载 1_isLoading防止同一时间多次请求_hasMore标记是否有更多数据无数据时隐藏加载动画。异步请求模拟网络请求的Future.delayed实际开发中替换为真实的网络请求如 Dio逻辑不变。五、列表开发性能优化最佳实践企业级列表是 APP 性能的核心瓶颈尤其是海量数据 / 复杂列表项以下是 Flutter 官方推荐的性能优化技巧开发中必须遵守1. 始终使用懒加载构建用ListView.builder/GridView.builder替代直接ListView(children: [])仅创建可见区域的组件即使只有几十条数据也使用懒加载养成良好习惯。2. 固定列表项尺寸ListView 设置itemExtent固定高度GridView 通过childAspectRatio固定宽高比避免 Flutter实时计算列表项尺寸大幅提升滚动流畅度。3. 优化图片加载网络图片使用缓存框架如cached_network_image避免重复加载设置图片宽高固定避免拉伸 / 变形导致的性能消耗大列表使用图片懒加载仅加载可见区域的图片。4. 减少列表项的 Widget 嵌套自定义列表项时尽量减少 Row/Column/Container 的嵌套层级避免在列表项中使用复杂的布局组件如Expanded过多。5. 规范管理资源ScrollController/TextEditingController等必须在dispose中销毁列表项中的监听器 / 定时器在列表项销毁时及时移除。6. 避免在 build 中创建对象不要在itemBuilder中创建新对象 / 方法如onTap: () {}除外列表项的构建方法抽离为全局方法避免每次 build 创建新的对象实例。7. 使用 const 构造函数列表项中的静态 Widget如 Text/Icon使用const修饰复用 Widget 实例例const Text(测试)而非Text(测试)。六、本节课核心总结必背列表开发全考点1. ListView 核心懒加载核心ListView.builder必传itemCount和itemBuilder横向列表scrollDirection: Axis.horizontal必须指定高度快速列表项ListTileleading/title/subtitle/trailing/onTap自定义列表项Row/Column/Container 组合注意文本溢出maxLinesoverflow。2. GridView 核心懒加载核心GridView.builder布局委托SliverGridDelegateWithFixedCrossAxisCount固定列数开发首选核心参数crossAxisCount列数、childAspectRatio宽高比、mainAxisSpacing/crossAxisSpacing间距视觉优化网格项白色背景 轻微阴影页面浅灰色背景。3. 下拉刷新 上拉加载 核心下拉刷新RefreshIndicator包裹列表onRefresh为异步方法上拉加载ScrollController监听滚动判断position.pixels maxScrollExtent - 50状态三要素_isLoading防重复加载、_hasMore无更多数据、_page页码资源管理ScrollController必须在dispose中销毁。4. 性能优化 核心懒加载 固定尺寸列表性能优化的两大基石图片缓存 减少嵌套自定义列表项的优化关键规范销毁资源避免内存泄漏保证应用长期运行稳定异步请求判mounted防止组件销毁后更新状态导致崩溃。七、课后练习列表实战必备必敲代码基础练习实现一个九宫格相册列表GridView 3 列正方形加载 30 张随机图片点击图片弹出提示进阶练习在自定义商品列表项中集成下拉刷新 上拉加载实现商品列表的刷新和加载更多实战练习实现一个混合列表ListView 中嵌套 GridView 横向 ListView如 APP 首页顶部横向分类→GridView 商品推荐→ListView 最新资讯要求所有列表都支持滚动联动。下一节课预告我们会学习 Flutter 的网络请求与数据解析开发必备包括Dio 框架的使用Flutter 最主流网络请求库、GET/POST 请求、请求拦截器统一添加请求头、处理错误、JSON 数据解析手动解析 json_serializable 自动解析、网络请求与列表结合真实接口实现列表的下拉刷新 / 上拉加载学会后就能对接后端接口实现真实的网络数据交互让你的 Flutter 应用真正活起来列表是 Flutter 开发的核心建议多敲代码调试重点掌握懒加载和下拉刷新 / 上拉加载的逻辑这是后续对接真实接口的基础