本代码记录的TabBar+TabBarView+分页SmartRefresher的实现方案,
实现了第一次切换tab时加载数据,记录每个tabbarview的滚动位置,
dart
import 'package:ayidaojia/common/index.dart';
import 'package:ducafe_ui_core/ducafe_ui_core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart';
import 'index.dart';
class OrderListPage extends GetView<OrderListController> {
const OrderListPage({super.key});
// tab 视图
Widget _buildTabBar() {
return Container(
height: 88.w,
color: AppTheme.blockBgColor,
child: TabBar(
controller: controller.tabController,
isScrollable: true,
indicatorColor: AppTheme.primary,
indicatorSize: TabBarIndicatorSize.label,
indicatorWeight: 4.w,
labelColor: AppTheme.primary,
unselectedLabelColor: AppTheme.color999,
labelStyle: TextStyle(fontSize: 28.sp, fontWeight: FontWeight.bold),
unselectedLabelStyle: TextStyle(fontSize: 28.sp, fontWeight: FontWeight.normal),
tabs: controller.tabNames.map((name) {
return Tab(text: name);
}).toList(),
),
);
}
// TabBarView 内容
Widget _buildTabBarView() {
return Expanded(
child: TabBarView(
controller: controller.tabController,
children: List.generate(
controller.tabNames.length,
(index) => _OrderListTab(tabIndex: index),
),
),
);
}
// 主视图
Widget _buildView() {
return <Widget>[
_buildTabBar(),
_buildTabBarView()
].toColumn();
}
@override
Widget build(BuildContext context) {
return GetBuilder<OrderListController>(
init: OrderListController(),
id: "order_list",
builder: (_) {
return Scaffold(
appBar: MineNavBar(
titleWidget: TextWidget.body('我的订单', size: 32.sp, color: AppTheme.colorfff, weight: FontWeight.w600),
),
body: _buildView(),
);
},
);
}
}
// 订单列表 Tab 页面(带缓存)
class _OrderListTab extends StatefulWidget {
final int tabIndex;
const _OrderListTab({required this.tabIndex});
@override
State<_OrderListTab> createState() => _OrderListTabState();
}
class _OrderListTabState extends State<_OrderListTab> with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true; // 保持页面状态
OrderListController get controller => Get.find<OrderListController>();
// 订单列表
Widget _buildList() {
return GetBuilder<OrderListController>(
id: "order_list",
builder: (_) {
return SmartRefresher(
controller: controller.getRefreshController(widget.tabIndex),
enablePullUp: true, // 启用上拉加载
onRefresh: () => controller.onRefresh(widget.tabIndex), // 下拉刷新回调
onLoading: () => controller.onLoading(widget.tabIndex), // 上拉加载回调
header: const SmartRefresherHeaderWidget(), // 头部刷新组件
footer: const SmartRefresherFooterWidget(), // 底部加载更多组件
child: ListView.separated(
padding: EdgeInsets.symmetric(horizontal: 30.w, vertical: 20.w),
itemBuilder: (context, index) {
return _buildOrderItem(index);
},
separatorBuilder: (context, index) {
return SizedBox(height: 20.w);
},
itemCount: controller.getItems(widget.tabIndex).length,
),
);
},
);
}
// 订单item
Widget _buildOrderItem(int itemIndex) {
final item = controller.getItems(widget.tabIndex)[itemIndex] as Map<String, dynamic>;
return <Widget>[
// 订单编号和状态
<Widget>[
TextWidget.body(
'订单号: ${item['orderNo']}',
size: 24.sp,
color: AppTheme.color999,
),
TextWidget.body(
item['statusText'],
size: 24.sp,
color: AppTheme.colorfff,
),
].toRow(mainAxisAlignment: MainAxisAlignment.spaceBetween),
SizedBox(height: 20.w),
// 服务名称
TextWidget.body(
item['serviceName'],
size: 28.sp,
color: AppTheme.colorfff,
weight: FontWeight.bold,
),
SizedBox(height: 16.w),
// 服务类型
TextWidget.body(
'服务类型: ${item['serviceType']}',
size: 24.sp,
color: AppTheme.color999,
),
SizedBox(height: 16.w),
// 阿姨信息
TextWidget.body(
'服务阿姨: ${item['workerName']}',
size: 24.sp,
color: AppTheme.color999,
),
SizedBox(height: 16.w),
// 价格和时间
<Widget>[
TextWidget.body(
'¥${item['price']}',
size: 32.sp,
color: AppTheme.colorfff,
weight: FontWeight.bold,
),
TextWidget.body(
item['createTime'],
size: 20.sp,
color: AppTheme.color666,
),
].toRow(mainAxisAlignment: MainAxisAlignment.spaceBetween),
].toColumn(crossAxisAlignment: CrossAxisAlignment.start)
.padding(all: 24.w)
.tight(height: 380.w)
.backgroundColor(AppTheme.blockBgColor)
.clipRRect(all: 20.w);
}
@override
Widget build(BuildContext context) {
super.build(context); // 必须调用 super.build
return _buildList();
}
}
dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart';
class OrderListController extends GetxController with GetSingleTickerProviderStateMixin {
OrderListController();
// TabController
late TabController tabController;
// tab 名称
List<String> tabNames = ['全部', '待支付', '待接单', '待使用', '待评价','已完成'];
// 每个 tab 的刷新控制器
late List<RefreshController> refreshControllers;
// 每个 tab 的订单列表
late List<List> itemsList;
// 每个 tab 的分页页码
late List<int> pageList;
// 每个 tab 是否已加载过数据
late List<bool> hasLoadedList;
// 初始化数据
void _initData() {
// 初始化 TabController
int initialIndex = Get.arguments?["tabIndex"] ?? 0;
tabController = TabController(
length: tabNames.length,
vsync: this,
initialIndex: initialIndex,
);
// 初始化每个 tab 的刷新控制器
refreshControllers = List.generate(
tabNames.length,
(index) => RefreshController(initialRefresh: false),
);
// 初始化每个 tab 的订单列表
itemsList = List.generate(tabNames.length, (index) => []);
// 初始化每个 tab 的分页页码
pageList = List.generate(tabNames.length, (index) => 1);
// 初始化每个 tab 的加载状态
hasLoadedList = List.generate(tabNames.length, (index) => false);
// 监听 tab 切换
tabController.addListener(_onTabChanged);
// 首次加载当前 tab 数据
_loadTabDataIfNeeded(initialIndex);
update(["order_list"]);
}
// tab 切换监听
void _onTabChanged() {
if (!tabController.indexIsChanging) {
_loadTabDataIfNeeded(tabController.index);
}
}
// 如果 tab 未加载过数据,则触发加载
void _loadTabDataIfNeeded(int tabIndex) {
if (!hasLoadedList[tabIndex]) {
hasLoadedList[tabIndex] = true;
// 延迟一帧执行,确保 UI 已经构建完成
Future.delayed(const Duration(milliseconds: 100), () {
refreshControllers[tabIndex].requestRefresh();
});
}
}
// 获取指定 tab 的刷新控制器
RefreshController getRefreshController(int tabIndex) {
return refreshControllers[tabIndex];
}
// 获取指定 tab 的订单列表
List getItems(int tabIndex) {
return itemsList[tabIndex];
}
/*
* 分页
* tabIndex: tab 索引
* isRefresh: 是否刷新
* */
Future<bool> _loadOrderList(int tabIndex, bool isRefresh) async {
// 模拟网络延迟
await Future.delayed(const Duration(milliseconds: 800));
// 模拟数据
List<Map<String, dynamic>> mockData = List.generate(10, (index) {
int currentPage = isRefresh ? 1 : pageList[tabIndex];
int orderNum = (currentPage - 1) * 10 + index + 1;
return {
'orderId': 'ORD${tabNames[tabIndex]}${orderNum.toString().padLeft(4, '0')}',
'orderNo': '202412${orderNum.toString().padLeft(8, '0')}',
'serviceName': '${tabNames[tabIndex]} - 家政服务 $orderNum',
'serviceType': '保洁服务',
'price': (99 + orderNum * 10).toDouble(),
'status': tabIndex,
'statusText': tabNames[tabIndex],
'createTime': '2024-12-02 ${(10 + index).toString().padLeft(2, '0')}:30:00',
'workerName': '阿姨 ${String.fromCharCode(65 + index)}',
'workerAvatar': 'https://picsum.photos/100/100?random=$orderNum',
};
});
if (isRefresh) {
pageList[tabIndex] = 1;
itemsList[tabIndex].clear();
}
// 模拟分页:第3页后没有更多数据
if (pageList[tabIndex] >= 3) {
return true; // 返回 true 表示没有更多数据
}
if (mockData.isNotEmpty) {
pageList[tabIndex]++;
itemsList[tabIndex].addAll(mockData);
}
return mockData.isEmpty;
// TODO: 调用接口获取数据
// var result = await MallApi.getOrderList(PageListReq(
// page: isRefresh ? 1 : pageList[tabIndex],
// limit: 20,
// status: tabIndex.toString(),
// ));
// if (isRefresh) {
// pageList[tabIndex] = 1;
// itemsList[tabIndex].clear();
// }
// if (result.isNotEmpty) {
// pageList[tabIndex]++;
// itemsList[tabIndex].addAll(result);
// }
// return result.isEmpty;
}
// 上拉加载
void onLoading(int tabIndex) async {
if (itemsList[tabIndex].isNotEmpty) {
try {
// 拉取数据是否为空 ? 设置暂无数据 : 加载完成
var isEmpty = await _loadOrderList(tabIndex, false);
isEmpty
? refreshControllers[tabIndex].loadNoData()
: refreshControllers[tabIndex].loadComplete();
} catch (e) {
refreshControllers[tabIndex].loadFailed(); // 加载失败
}
} else {
refreshControllers[tabIndex].loadNoData(); // 设置无数据
}
update(["order_list"]);
}
// 下拉刷新
void onRefresh(int tabIndex) async {
try {
await _loadOrderList(tabIndex, true);
refreshControllers[tabIndex].refreshCompleted();
} catch (e) {
refreshControllers[tabIndex].refreshFailed();
}
update(["order_list"]);
}
@override
void onReady() {
super.onReady();
_initData();
}
@override
void onClose() {
// 释放 TabController
tabController.dispose();
// 释放所有刷新控制器
for (var controller in refreshControllers) {
controller.dispose();
}
super.onClose();
}
}