|
|
@ -1,8 +1,7 @@ |
|
|
|
|
|
import 'package:easy_refresh/easy_refresh.dart'; |
|
|
import 'package:flutter/material.dart'; |
|
|
import 'package:flutter/material.dart'; |
|
|
import 'package:get/get.dart'; |
|
|
import 'package:get/get.dart'; |
|
|
import 'package:pull_to_refresh/pull_to_refresh.dart'; |
|
|
|
|
|
import 'package:dating_touchme_app/controller/home/home_controller.dart'; |
|
|
import 'package:dating_touchme_app/controller/home/home_controller.dart'; |
|
|
import 'package:dating_touchme_app/model/home/marriage_data.dart'; |
|
|
|
|
|
import 'package:dating_touchme_app/pages/home/content_card.dart'; |
|
|
import 'package:dating_touchme_app/pages/home/content_card.dart'; |
|
|
|
|
|
|
|
|
/// 同城列表 Tab |
|
|
/// 同城列表 Tab |
|
|
@ -13,15 +12,14 @@ class NearbyTab extends StatefulWidget { |
|
|
State<NearbyTab> createState() => _NearbyTabState(); |
|
|
State<NearbyTab> createState() => _NearbyTabState(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
class _NearbyTabState extends State<NearbyTab> |
|
|
|
|
|
with AutomaticKeepAliveClientMixin { |
|
|
|
|
|
|
|
|
class _NearbyTabState extends State<NearbyTab> with AutomaticKeepAliveClientMixin { |
|
|
final HomeController controller = Get.find<HomeController>(); |
|
|
final HomeController controller = Get.find<HomeController>(); |
|
|
late final RefreshController _refreshController; |
|
|
|
|
|
|
|
|
late final EasyRefreshController _refreshController; |
|
|
|
|
|
|
|
|
@override |
|
|
@override |
|
|
void initState() { |
|
|
void initState() { |
|
|
super.initState(); |
|
|
super.initState(); |
|
|
_refreshController = RefreshController(initialRefresh: false); |
|
|
|
|
|
|
|
|
_refreshController = EasyRefreshController(controlFinishRefresh: true, controlFinishLoad: true); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@override |
|
|
@override |
|
|
@ -38,104 +36,97 @@ class _NearbyTabState extends State<NearbyTab> |
|
|
final tabBarHeight = 64.0; |
|
|
final tabBarHeight = 64.0; |
|
|
final totalBottomPadding = bottomPadding + tabBarHeight; |
|
|
final totalBottomPadding = bottomPadding + tabBarHeight; |
|
|
|
|
|
|
|
|
return SizedBox( |
|
|
|
|
|
height: MediaQuery.of(context).size.height - totalBottomPadding, |
|
|
|
|
|
child: Obx(() { |
|
|
|
|
|
final List<MarriageData> dataSource = controller.nearbyFeed; |
|
|
|
|
|
|
|
|
|
|
|
return SmartRefresher( |
|
|
|
|
|
|
|
|
return Obx(() { |
|
|
|
|
|
return EasyRefresh( |
|
|
controller: _refreshController, |
|
|
controller: _refreshController, |
|
|
enablePullDown: true, |
|
|
|
|
|
enablePullUp: true, |
|
|
|
|
|
header: const ClassicHeader( |
|
|
header: const ClassicHeader( |
|
|
refreshingText: '正在刷新...', |
|
|
|
|
|
completeText: '刷新完成', |
|
|
|
|
|
idleText: '下拉刷新', |
|
|
|
|
|
releaseText: '释放刷新', |
|
|
|
|
|
|
|
|
dragText: '下拉刷新', |
|
|
|
|
|
armedText: '释放刷新', |
|
|
|
|
|
readyText: '刷新中...', |
|
|
|
|
|
processingText: '刷新中...', |
|
|
|
|
|
processedText: '刷新完成', |
|
|
|
|
|
failedText: '刷新失败', |
|
|
|
|
|
noMoreText: '没有更多数据', |
|
|
|
|
|
showMessage: false |
|
|
), |
|
|
), |
|
|
footer: ClassicFooter( |
|
|
footer: ClassicFooter( |
|
|
idleText: '上拉加载', |
|
|
|
|
|
loadingText: '正在加载...', |
|
|
|
|
|
noDataText: '没有更多数据', |
|
|
|
|
|
canLoadingText: '释放加载', |
|
|
|
|
|
|
|
|
dragText: '上拉加载', |
|
|
|
|
|
armedText: '释放加载', |
|
|
|
|
|
readyText: '加载中...', |
|
|
|
|
|
processingText: '加载中...', |
|
|
|
|
|
processedText: '加载完成', |
|
|
|
|
|
failedText: '加载失败', |
|
|
|
|
|
noMoreText: '没有更多数据', |
|
|
|
|
|
showMessage: false |
|
|
), |
|
|
), |
|
|
// 下拉刷新 |
|
|
// 下拉刷新 |
|
|
onRefresh: () async { |
|
|
onRefresh: () async { |
|
|
print('同城列表下拉刷新被触发'); |
|
|
print('同城列表下拉刷新被触发'); |
|
|
try { |
|
|
try { |
|
|
await controller.refreshNearbyData(); |
|
|
await controller.refreshNearbyData(); |
|
|
_refreshController.refreshCompleted(); |
|
|
|
|
|
_refreshController.loadComplete(); |
|
|
|
|
|
|
|
|
_refreshController.finishRefresh(); |
|
|
|
|
|
_refreshController.resetFooter(); |
|
|
print('同城列表刷新完成'); |
|
|
print('同城列表刷新完成'); |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
print('同城列表刷新失败: $e'); |
|
|
print('同城列表刷新失败: $e'); |
|
|
_refreshController.refreshFailed(); |
|
|
|
|
|
|
|
|
_refreshController.finishRefresh(IndicatorResult.fail); |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
// 上拉加载更多 |
|
|
// 上拉加载更多 |
|
|
onLoading: () async { |
|
|
|
|
|
|
|
|
onLoad: () async { |
|
|
print('同城列表上拉加载被触发, hasMore: ${controller.nearbyHasMore.value}'); |
|
|
print('同城列表上拉加载被触发, hasMore: ${controller.nearbyHasMore.value}'); |
|
|
if (controller.nearbyHasMore.value) { |
|
|
|
|
|
try { |
|
|
|
|
|
await controller.loadNearbyMoreData(); |
|
|
|
|
|
// 完成加载,根据是否有更多数据决定 |
|
|
|
|
|
if (controller.nearbyHasMore.value) { |
|
|
|
|
|
_refreshController.loadComplete(); |
|
|
|
|
|
print('同城列表加载更多成功'); |
|
|
|
|
|
} else { |
|
|
|
|
|
_refreshController.loadNoData(); |
|
|
|
|
|
print('同城列表没有更多数据了'); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
print('同城列表加载更多失败: $e'); |
|
|
|
|
|
_refreshController.loadFailed(); |
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
await controller.loadNearbyMoreData(); |
|
|
|
|
|
// 完成加载,根据是否有更多数据决定 |
|
|
|
|
|
if (controller.nearbyHasMore.value) { |
|
|
|
|
|
_refreshController.finishLoad(IndicatorResult.success); |
|
|
|
|
|
print('同城列表加载更多成功'); |
|
|
|
|
|
} else { |
|
|
|
|
|
_refreshController.finishLoad(IndicatorResult.noMore); |
|
|
|
|
|
print('同城列表没有更多数据了'); |
|
|
} |
|
|
} |
|
|
} else { |
|
|
|
|
|
_refreshController.loadNoData(); |
|
|
|
|
|
print('同城列表没有更多数据'); |
|
|
|
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
print('同城列表加载更多失败: $e'); |
|
|
|
|
|
_refreshController.finishLoad(IndicatorResult.fail); |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
child: ListView.separated( |
|
|
|
|
|
// 关键:始终允许滚动,即使内容不足 |
|
|
|
|
|
physics: const AlwaysScrollableScrollPhysics( |
|
|
|
|
|
parent: BouncingScrollPhysics(), |
|
|
|
|
|
), |
|
|
|
|
|
// 移除顶部 padding,让刷新指示器可以正确显示在 AppBar 下方 |
|
|
|
|
|
padding: EdgeInsets.only( |
|
|
|
|
|
left: 12, |
|
|
|
|
|
right: 12, |
|
|
|
|
|
bottom: 12, |
|
|
|
|
|
), |
|
|
|
|
|
itemBuilder: (context, index) { |
|
|
|
|
|
// 空数据状态 |
|
|
|
|
|
if (dataSource.isEmpty && index == 0) { |
|
|
|
|
|
// 使用足够的高度确保可以滚动 |
|
|
|
|
|
return SizedBox( |
|
|
|
|
|
height: MediaQuery.of(context).size.height - totalBottomPadding, |
|
|
|
|
|
child: const Center( |
|
|
|
|
|
child: Text( |
|
|
|
|
|
"暂无数据", |
|
|
|
|
|
style: TextStyle(fontSize: 14, color: Color(0xFF999999)), |
|
|
|
|
|
|
|
|
child: SizedBox( |
|
|
|
|
|
height: MediaQuery.of(context).size.height - totalBottomPadding, |
|
|
|
|
|
child: ListView.separated( |
|
|
|
|
|
// 移除顶部 padding,让刷新指示器可以正确显示在 AppBar 下方 |
|
|
|
|
|
padding: EdgeInsets.only(left: 12, right: 12, bottom: 12), |
|
|
|
|
|
itemBuilder: (context, index) { |
|
|
|
|
|
// 空数据状态 |
|
|
|
|
|
if (controller.nearbyFeed.isEmpty && index == 0) { |
|
|
|
|
|
// 使用足够的高度确保可以滚动 |
|
|
|
|
|
return SizedBox( |
|
|
|
|
|
height: MediaQuery.of(context).size.height - totalBottomPadding, |
|
|
|
|
|
child: const Center( |
|
|
|
|
|
child: Text( |
|
|
|
|
|
"暂无数据", |
|
|
|
|
|
style: TextStyle(fontSize: 14, color: Color(0xFF999999)), |
|
|
|
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
// 数据项 |
|
|
|
|
|
final item = dataSource[index]; |
|
|
|
|
|
return ContentCard(item: item); |
|
|
|
|
|
}, |
|
|
|
|
|
separatorBuilder: (context, index) { |
|
|
|
|
|
// 空状态或加载状态时不显示分隔符 |
|
|
|
|
|
if (dataSource.isEmpty) return const SizedBox.shrink(); |
|
|
|
|
|
return const SizedBox(height: 12); |
|
|
|
|
|
}, |
|
|
|
|
|
// 至少显示一个 item(用于显示加载或空状态) |
|
|
|
|
|
itemCount: dataSource.isEmpty ? 1 : dataSource.length, |
|
|
|
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
// 数据项 |
|
|
|
|
|
final item = controller.nearbyFeed[index]; |
|
|
|
|
|
return ContentCard(item: item); |
|
|
|
|
|
}, |
|
|
|
|
|
separatorBuilder: (context, index) { |
|
|
|
|
|
// 空状态或加载状态时不显示分隔符 |
|
|
|
|
|
if (controller.nearbyFeed.isEmpty) return const SizedBox.shrink(); |
|
|
|
|
|
return const SizedBox(height: 12); |
|
|
|
|
|
}, |
|
|
|
|
|
// 至少显示一个 item(用于显示加载或空状态) |
|
|
|
|
|
itemCount: controller.nearbyFeed.isEmpty ? 1 : controller.nearbyFeed.length, |
|
|
|
|
|
) |
|
|
), |
|
|
), |
|
|
); |
|
|
); |
|
|
})); |
|
|
|
|
|
|
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@override |
|
|
@override |
|
|
bool get wantKeepAlive => true; |
|
|
bool get wantKeepAlive => true; |
|
|
|
|
|
|
|
|
} |
|
|
} |