在 Flutter 开发中,列表(商品列表、消息列表、订单列表)是高频场景。原生RefreshIndicator仅支持下拉刷新,上拉加载需手动监听滚动、管理加载状态,且空数据、错误等异常状态需重复开发。本文封装的CommonRefreshList整合 "下拉刷新 + 上拉加载 + 空状态 + 错误状态 + 加载中状态" 五大核心能力,支持分页逻辑、自定义状态样式,一行代码集成,覆盖 95%+ 列表场景,彻底解放重复编码!
一、核心优势(精准解决开发痛点)
✅ 状态全适配:内置加载中、空数据、错误、无更多数据 4 种异常状态,无需手动判断切换✅ 刷新加载整合:下拉刷新与上拉加载逻辑封装,无需单独处理滚动监听和状态管理✅ 分页逻辑内置:支持页码 / 游标分页,自动管理pageIndex和hasMore状态,减少重复代码✅ 高扩展性:下拉刷新样式、上拉加载提示、各状态页面均可自定义,适配不同设计风格✅ 性能优化:列表项复用、加载状态防重复触发(避免多次请求),适配大数据列表✅ 交互友好:错误状态支持点击重试、下拉刷新动画流畅、上拉加载触发阈值合理,贴合用户习惯
二、核心配置速览(关键参数一目了然)
| 配置分类 | 核心参数 | 核心作用 |
|---|---|---|
| 必选配置 | itemBuilder、onLoadData |
列表项构建器(渲染单个列表项)、数据加载回调(分页请求数据) |
| 刷新配置 | enablePullDown、onRefresh |
是否启用下拉刷新(默认 true)、自定义刷新回调(优先级高于默认逻辑) |
| 加载配置 | enablePullUp、pageSize、hasMore |
是否启用上拉加载(默认 true)、每页数据量(默认 10)、是否有更多数据(外部控制) |
| 状态配置 | emptyWidget、errorWidget等 |
空数据、错误、加载中、无更多数据的自定义组件(支持个性化设计) |
| 列表配置 | controller、itemExtent、padding |
滚动控制器(外部监听滚动)、列表项固定高度(优化性能)、内边距 |
三、生产级完整代码(可直接复制,开箱即用)
dart
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
/// 列表加载状态枚举(统一管理所有状态,逻辑清晰)
enum ListLoadStatus {
loading, // 初始加载中
empty, // 空数据
error, // 加载错误
success, // 加载成功(有数据)
noMore // 上拉加载无更多
}
/// 通用列表刷新加载组件(支持下拉刷新、上拉加载、多状态适配)
class CommonRefreshList<T> extends StatefulWidget {
// 必选参数(核心依赖)
final Widget Function(BuildContext, T, int) itemBuilder; // 列表项构建器(context, 数据, 索引)
final Future<List<T>> Function(int pageIndex, int pageSize) onLoadData; // 数据加载回调(页码, 页大小)
// 刷新配置(下拉刷新相关)
final bool enablePullDown; // 是否启用下拉刷新(默认true)
final Future<void> Function()? onRefresh; // 自定义下拉刷新回调(优先级高于默认逻辑)
final Color refreshColor; // 刷新指示器颜色(默认蓝色)
final double refreshTriggerDistance; // 下拉刷新触发距离(默认100px)
// 加载配置(上拉加载相关)
final bool enablePullUp; // 是否启用上拉加载(默认true)
final int pageSize; // 每页数据量(默认10)
final int initialPageIndex; // 初始页码(默认1)
final bool hasMore; // 是否有更多数据(外部控制,默认true)
final String loadMoreText; // 上拉加载提示文本(默认"正在加载更多...")
final String noMoreText; // 无更多数据提示文本(默认"没有更多数据了")
final TextStyle loadTextStyle; // 加载提示文本样式(默认14号灰色)
// 状态配置(各异常状态组件)
final Widget? loadingWidget; // 初始加载中组件(自定义样式)
final Widget? emptyWidget; // 空数据组件(自定义样式)
final Widget? errorWidget; // 错误组件(点击可重试,自定义样式)
final Widget? noMoreWidget; // 无更多数据组件(自定义样式)
// 列表配置(基础样式与性能优化)
final ScrollController? controller; // 滚动控制器(外部传入可监听滚动位置)
final ScrollPhysics? physics; // 滚动物理效果(默认适配平台)
final EdgeInsetsGeometry? padding; // 列表内边距(默认无)
final double? itemExtent; // 列表项固定高度(优化滚动性能,推荐设置)
final bool shrinkWrap; // 是否适应子组件高度(默认false,避免列表高度异常)
const CommonRefreshList({
super.key,
required this.itemBuilder,
required this.onLoadData,
// 刷新配置
this.enablePullDown = true,
this.onRefresh,
this.refreshColor = Colors.blue,
this.refreshTriggerDistance = 100.0,
// 加载配置
this.enablePullUp = true,
this.pageSize = 10,
this.initialPageIndex = 1,
this.hasMore = true,
this.loadMoreText = "正在加载更多...",
this.noMoreText = "没有更多数据了",
this.loadTextStyle = const TextStyle(fontSize: 14, color: Colors.grey),
// 状态配置
this.loadingWidget,
this.emptyWidget,
this.errorWidget,
this.noMoreWidget,
// 列表配置
this.controller,
this.physics,
this.padding,
this.itemExtent,
this.shrinkWrap = false,
});
@override
State<CommonRefreshList<T>> createState() => _CommonRefreshListState<T>();
}
class _CommonRefreshListState<T> extends State<CommonRefreshList<T>> {
late ScrollController _scrollController; // 滚动控制器(复用外部传入或新建)
late List<T> _dataList; // 列表数据源
late int _currentPage; // 当前页码
late ListLoadStatus _loadStatus; // 列表加载状态
bool _isLoadingMore = false; // 上拉加载锁(防重复请求)
bool _isRefreshing = false; // 下拉刷新锁(防重复请求)
@override
void initState() {
super.initState();
// 初始化滚动控制器(外部传入则复用,内部新建则自行管理生命周期)
_scrollController = widget.controller ?? ScrollController();
// 初始化数据与状态
_dataList = [];
_currentPage = widget.initialPageIndex;
_loadStatus = ListLoadStatus.loading;
// 监听滚动事件(触发上拉加载)
_scrollController.addListener(_onScroll);
// 初始加载数据
_initLoadData();
}
@override
void didUpdateWidget(covariant CommonRefreshList<T> oldWidget) {
super.didUpdateWidget(oldWidget);
// 外部控制hasMore变化时,恢复加载状态(支持重新加载更多)
if (widget.hasMore != oldWidget.hasMore && _loadStatus == ListLoadStatus.noMore) {
setState(() => _loadStatus = ListLoadStatus.success);
}
}
@override
void dispose() {
// 外部传入的控制器由外部管理,内部新建的需手动释放
if (widget.controller == null) _scrollController.dispose();
super.dispose();
}
/// 初始加载数据(首次进入页面触发)
Future<void> _initLoadData() async {
try {
final data = await widget.onLoadData(_currentPage, widget.pageSize);
setState(() {
_dataList = data;
// 根据返回数据判断状态:空数据→empty,有数据→success
_loadStatus = data.isEmpty ? ListLoadStatus.empty : ListLoadStatus.success;
});
} catch (e) {
setState(() => _loadStatus = ListLoadStatus.error);
EasyLoading.showError("加载失败:${e.toString()}");
}
}
/// 下拉刷新逻辑(重置页码,重新加载第一页)
Future<void> _handleRefresh() async {
if (_isRefreshing) return; // 防重复刷新
_isRefreshing = true;
try {
_currentPage = widget.initialPageIndex; // 重置页码
final newData = await widget.onLoadData(_currentPage, widget.pageSize);
setState(() {
_dataList = newData;
_loadStatus = newData.isEmpty ? ListLoadStatus.empty : ListLoadStatus.success;
});
} catch (e) {
EasyLoading.showError("刷新失败:${e.toString()}");
} finally {
_isRefreshing = false; // 释放刷新锁
}
}
/// 上拉加载逻辑(页码+1,追加数据)
Future<void> _handleLoadMore() async {
// 防重复加载:正在加载中/无更多数据/非成功状态→不触发
if (_isLoadingMore || !widget.hasMore || _loadStatus != ListLoadStatus.success) return;
_isLoadingMore = true;
try {
_currentPage++; // 页码自增
final newData = await widget.onLoadData(_currentPage, widget.pageSize);
setState(() {
if (newData.isEmpty) {
_loadStatus = ListLoadStatus.noMore; // 无更多数据
} else {
_dataList.addAll(newData); // 追加新数据
}
});
} catch (e) {
_currentPage--; // 加载失败回退页码(避免跳过当前页)
EasyLoading.showError("加载更多失败:${e.toString()}");
} finally {
_isLoadingMore = false; // 释放加载锁
}
}
/// 滚动监听(判断是否触发上拉加载)
void _onScroll() {
if (!widget.enablePullUp) return;
// 滚动到列表底部100px内,且未在加载中→触发加载更多
if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 100 && !_isLoadingMore) {
_handleLoadMore();
}
}
/// 重试加载(错误状态点击触发)
void _onRetry() {
setState(() => _loadStatus = ListLoadStatus.loading);
_initLoadData();
}
/// 构建初始加载中组件(默认+自定义适配)
Widget _buildLoadingWidget() {
return widget.loadingWidget ??
const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(strokeWidth: 2),
SizedBox(height: 16),
Text("正在加载中...", style: TextStyle(color: Colors.grey)),
],
),
);
}
/// 构建空数据组件(默认+自定义适配)
Widget _buildEmptyWidget() {
return widget.emptyWidget ??
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.inbox_outlined, size: 64, color: Colors.grey[300]),
const SizedBox(height: 16),
const Text("暂无数据", style: TextStyle(color: Colors.grey, fontSize: 16)),
],
),
);
}
/// 构建错误组件(默认+自定义适配,支持点击重试)
Widget _buildErrorWidget() {
return widget.errorWidget ??
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, size: 64, color: Colors.grey[300]),
const SizedBox(height: 16),
const Text("加载失败", style: TextStyle(color: Colors.grey, fontSize: 16)),
const SizedBox(height: 8),
TextButton(
onPressed: _onRetry,
child: const Text("点击重试"),
),
],
),
);
}
/// 构建无更多数据组件(默认+自定义适配)
Widget _buildNoMoreWidget() {
return widget.noMoreWidget ??
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Center(child: Text(widget.noMoreText, style: widget.loadTextStyle)),
);
}
/// 构建上拉加载提示组件(加载中状态)
Widget _buildLoadMoreWidget() {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(strokeWidth: 2),
const SizedBox(width: 8),
Text(widget.loadMoreText, style: widget.loadTextStyle),
],
),
),
);
}
/// 构建列表主体(包含正常列表项+加载更多/无更多提示)
Widget _buildListBody() {
return ListView.builder(
controller: _scrollController,
physics: widget.physics,
padding: widget.padding,
itemExtent: widget.itemExtent, // 固定高度优化性能
shrinkWrap: widget.shrinkWrap,
// 列表项数量=数据量+1(最后一项显示加载更多/无更多)
itemCount: _dataList.length + (widget.enablePullUp ? 1 : 0),
itemBuilder: (context, index) {
// 最后一项:显示加载更多或无更多
if (widget.enablePullUp && index == _dataList.length) {
return _loadStatus == ListLoadStatus.noMore ? _buildNoMoreWidget() : _buildLoadMoreWidget();
}
// 正常列表项:通过itemBuilder渲染
return widget.itemBuilder(context, _dataList[index], index);
},
);
}
@override
Widget build(BuildContext context) {
// 根据加载状态显示对应页面
Widget child;
switch (_loadStatus) {
case ListLoadStatus.loading:
child = _buildLoadingWidget();
break;
case ListLoadStatus.empty:
child = _buildEmptyWidget();
break;
case ListLoadStatus.error:
child = _buildErrorWidget();
break;
case ListLoadStatus.success:
case ListLoadStatus.noMore:
child = _buildListBody();
break;
}
// 包裹下拉刷新组件(启用时)
if (widget.enablePullDown) {
child = RefreshIndicator(
color: widget.refreshColor,
triggerMode: RefreshIndicatorTriggerMode.onEdge,
displacement: widget.refreshTriggerDistance,
onRefresh: widget.onRefresh ?? _handleRefresh, // 优先使用自定义刷新逻辑
child: child,
);
}
return child;
}
}
四、三大高频场景实战示例(直接复制可用)
场景 1:基础分页列表(商品列表,支持下拉刷新 + 上拉加载)
适用场景:电商商品列表、资讯列表等需要分页加载的场景
dart
class ProductListPage extends StatefulWidget {
@override
State<ProductListPage> createState() => _ProductListPageState();
}
class _ProductListPageState extends State<ProductListPage> {
bool _hasMore = true; // 控制是否有更多商品
// 商品数据模型(实际项目可替换为真实模型)
class Product {
final String id;
final String name;
final double price;
final String imageUrl;
Product({required this.id, required this.name, required this.price, required this.imageUrl});
}
// 模拟加载商品数据(实际项目替换为接口请求)
Future<List<Product>> _loadProductData(int pageIndex, int pageSize) async {
await Future.delayed(const Duration(1000)); // 模拟网络延迟
// 模拟第3页无更多数据
if (pageIndex >= 3) {
setState(() => _hasMore = false);
return [];
}
// 生成模拟数据
return List.generate(pageSize, (index) {
final realIndex = (pageIndex - 1) * pageSize + index;
return Product(
id: "prod_$realIndex",
name: "2025新款夏季T恤 $realIndex",
price: 99.0 + realIndex * 10,
imageUrl: "https://picsum.photos/200/200?random=$realIndex",
);
});
}
// 构建商品列表项
Widget _buildProductItem(BuildContext context, Product product, int index) {
return Container(
padding: const EdgeInsets.all(12),
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey[200]!),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 商品图片
ClipRRect(
borderRadius: BorderRadius.circular(6),
child: Image.network(
product.imageUrl,
width: 80,
height: 80,
fit: BoxFit.cover,
),
),
const SizedBox(width: 12),
// 商品信息
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 8),
Text(
"¥${product.price.toStringAsFixed(2)}",
style: const TextStyle(fontSize: 16, color: Colors.redAccent),
),
],
),
),
// 加入购物车按钮
IconButton(
icon: const Icon(Icons.add_shopping_cart, color: Colors.grey),
onPressed: () => EasyLoading.showToast("添加 ${product.name} 到购物车"),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("商品列表")),
body: CommonRefreshList<Product>(
itemBuilder: _buildProductItem,
onLoadData: _loadProductData,
hasMore: _hasMore,
pageSize: 8, // 每页8条数据
enablePullDown: true,
enablePullUp: true,
padding: const EdgeInsets.symmetric(vertical: 8),
// 自定义空状态组件(贴合商品场景)
emptyWidget: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.shopping_bag_outlined, size: 64, color: Colors.grey[300]),
SizedBox(height: 16),
Text("暂无商品数据", style: TextStyle(color: Colors.grey, fontSize: 16)),
SizedBox(height: 8),
Text("换个关键词试试吧~", style: TextStyle(color: Colors.grey[500], fontSize: 14)),
],
),
),
),
);
}
}
场景 2:无分页列表(消息列表,仅下拉刷新)
适用场景:消息列表、通知列表等无需分页,仅需下拉刷新的场景
dart
class MessageListPage extends StatefulWidget {
@override
State<MessageListPage> createState() => _MessageListPageState();
}
class _MessageListPageState extends State<MessageListPage> {
// 消息数据模型
class Message {
final String id;
final String title;
final String content;
final String time;
final bool isRead; // 是否已读
Message({
required this.id,
required this.title,
required this.content,
required this.time,
this.isRead = false,
});
}
// 加载消息数据(无分页,仅下拉刷新)
Future<List<Message>> _loadMessageData(int pageIndex, int pageSize) async {
await Future.delayed(const Duration(800)); // 模拟网络延迟
// 生成模拟消息数据
return List.generate(15, (index) {
return Message(
id: "msg_$index",
title: index % 3 == 0 ? "系统通知" : "好友消息",
content: "这是一条测试消息内容,用于展示列表项样式 $index",
time: "${10 + index}:${index * 5}",
isRead: index > 5, // 前5条为未读
);
});
}
// 构建消息列表项
Widget _buildMessageItem(BuildContext context, Message message, int index) {
return ListTile(
leading: CircleAvatar(
child: Text(message.title.substring(0, 1)),
backgroundColor: message.isRead ? Colors.grey[200] : Colors.blue,
foregroundColor: message.isRead ? Colors.grey : Colors.white,
),
title: Text(
message.title,
style: TextStyle(
fontWeight: message.isRead ? FontWeight.normal : FontWeight.bold,
),
),
subtitle: Text(
message.content,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: message.isRead ? Colors.grey : Colors.black87,
),
),
trailing: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(message.time, style: const TextStyle(fontSize: 12, color: Colors.grey)),
// 未读红点
if (!message.isRead)
const SizedBox(
width: 8,
height: 8,
child: CircleAvatar(backgroundColor: Colors.red),
),
],
),
onTap: () => EasyLoading.showToast("查看消息:${message.title}"),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("消息列表")),
body: CommonRefreshList<Message>(
itemBuilder: _buildMessageItem,
onLoadData: _loadMessageData,
enablePullDown: true,
enablePullUp: false, // 关闭上拉加载(无分页)
refreshColor: Colors.orangeAccent, // 自定义刷新颜色
// 自定义错误状态组件
errorWidget: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.message_outlined, size: 64, color: Colors.grey[300]),
const SizedBox(height: 16),
const Text("消息加载失败", style: TextStyle(color: Colors.grey, fontSize: 16)),
const SizedBox(height: 8),
ElevatedButton(
onPressed: () {},
child: const Text("重新加载"),
),
],
),
),
),
);
}
}
场景 3:固定高度列表(订单列表,优化滚动性能)
适用场景:订单列表、账单列表等列表项高度固定的场景(性能更优)
dart
class OrderListPage extends StatefulWidget {
@override
State<OrderListPage> createState() => _OrderListPageState();
}
class _OrderListPageState extends State<OrderListPage> {
bool _hasMore = true; // 控制是否有更多订单
// 订单数据模型
class Order {
final String orderNo;
final double amount;
final String status;
final String time;
Order({
required this.orderNo,
required this.amount,
required this.status,
required this.time,
});
}
// 加载订单数据
Future<List<Order>> _loadOrderData(int pageIndex, int pageSize) async {
await Future.delayed(const Duration(1000)); // 模拟网络延迟
// 模拟第4页无更多数据
if (pageIndex >= 4) {
setState(() => _hasMore = false);
return [];
}
// 生成模拟订单数据
return List.generate(pageSize, (index) {
final realIndex = (pageIndex - 1) * pageSize + index;
final statusList = ["待支付", "已支付", "已取消", "已完成"];
return Order(
orderNo: "ORDER${DateTime.now().year}${10000 + realIndex}",
amount: 50.0 + realIndex * 20,
status: statusList[realIndex % 4],
time: "2024-12-${10 + realIndex % 20} 1${realIndex % 9}:${realIndex % 59}",
);
});
}
// 构建订单状态标签
Widget _buildStatusTag(String status) {
Color color;
switch (status) {
case "待支付":
color = Colors.orangeAccent;
break;
case "已支付":
color = Colors.green;
break;
case "已取消":
color = Colors.grey;
break;
case "已完成":
color = Colors.blue;
break;
default:
color = Colors.grey;
}
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
status,
style: TextStyle(color: color, fontSize: 12),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("订单列表")),
body: CommonRefreshList<Order>(
// 构建订单列表项
itemBuilder: (context, order, index) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey[200]!),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 订单号和状态
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("订单号:${order.orderNo}", style: const TextStyle(fontSize: 14, color: Colors.grey)),
_buildStatusTag(order.status),
],
),
const SizedBox(height: 8),
// 订单金额
Text("订单金额:¥${order.amount.toStringAsFixed(2)}", style: const TextStyle(fontSize: 16)),
const SizedBox(height: 8),
// 下单时间和操作
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(order.time, style: const TextStyle(fontSize: 14, color: Colors.grey)),
TextButton(
onPressed: () => EasyLoading.showToast("查看订单详情:${order.orderNo}"),
child: const Text("查看详情"),
),
],
),
],
),
);
},
onLoadData: _loadOrderData,
hasMore: _hasMore,
pageSize: 6,
itemExtent: 140, // 固定列表项高度(大幅提升滚动性能)
padding: const EdgeInsets.symmetric(vertical: 8),
// 自定义无更多数据组件
noMoreWidget: Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Center(
child: Text("已加载全部订单", style: widget.loadTextStyle.copyWith(fontSize: 15)),
),
),
),
);
}
}
五、核心封装技巧(复用成熟设计思路)
- 状态分层管理 :通过
ListLoadStatus枚举统一管理 5 种状态,避免分散判断,状态切换逻辑清晰,外部无需关心内部状态流转。 - 防重复触发机制 :通过
_isLoadingMore和_isRefreshing加载锁,防止滚动或下拉时多次触发请求,避免接口压力和数据错乱。 - 分页逻辑解耦 :页码管理、数据追加、无更多判断等逻辑内置,外部仅需实现
onLoadData回调返回数据,无需重复编写分页逻辑。 - 组件插槽化设计:各状态页面(空、错误、加载中)支持外部自定义,兼顾通用性和个性化,适配不同 APP 设计风格。
- 性能优化细节 :支持
itemExtent固定列表项高度,减少 ListView 布局计算;复用外部传入的ScrollController,便于监听滚动位置实现吸顶等扩展功能。 - 错误恢复机制:上拉加载失败时自动回退页码,错误状态支持点击重试,提升用户体验,避免数据丢失。
六、避坑指南(解决 90% 开发痛点)
- 数据状态同步 :
hasMore需外部根据接口返回结果更新(如无更多数据时设为false),否则会持续触发上拉加载。 - 页码回退关键 :上拉加载失败时必须回退
_currentPage,否则下次加载会跳过当前页,导致数据断层。 - 控制器生命周期 :外部传入的
ScrollController需由外部管理dispose,内部新建的控制器会自动释放,避免内存泄漏。 - 空状态适配 :初始加载为空时显示空状态,下拉刷新后数据为空也会切换为空状态,需确保
onLoadData返回空列表时状态正确切换。 - 性能优化建议 :列表项高度固定时务必设置
itemExtent,大数据列表(超过 50 项)建议配合ListView.builder复用特性,避免卡顿。 - 自定义刷新逻辑 :如需自定义下拉刷新(如清除缓存),可通过
onRefresh回调实现,优先级高于默认逻辑,灵活适配特殊需求。
欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。