import 'package:cached_network_image/cached_network_image.dart'; import 'package:dating_touchme_app/extension/ex_widget.dart'; import 'package:dating_touchme_app/generated/assets.dart'; import 'package:dating_touchme_app/pages/home/event_info.dart'; import 'package:dating_touchme_app/pages/home/event_list.dart'; import 'package:dating_touchme_app/pages/home/matchmaker_item.dart'; import 'package:dating_touchme_app/pages/home/matchmaker_page.dart'; import 'package:dating_touchme_app/pages/mine/open_webview.dart'; import 'package:easy_refresh/easy_refresh.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:dating_touchme_app/controller/home/home_controller.dart'; import 'package:dating_touchme_app/pages/home/content_card.dart'; import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; /// 推荐列表 Tab class RecommendTab extends StatefulWidget { const RecommendTab({super.key}); @override State createState() => _RecommendTabState(); } class _RecommendTabState extends State with AutomaticKeepAliveClientMixin { final HomeController controller = Get.find(); late final EasyRefreshController _refreshController; @override void initState() { super.initState(); _refreshController = EasyRefreshController(controlFinishRefresh: true, controlFinishLoad: true); } @override void dispose() { _refreshController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { super.build(context); // 获取底部安全区域高度和 tabbar 高度(约64) final bottomPadding = MediaQuery.of(context).padding.bottom; final tabBarHeight = 64.0; final totalBottomPadding = bottomPadding + tabBarHeight; return Obx(() { if (controller.recommendIsLoading.value && controller.recommendFeed.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('加载数据中...'), ], ), ); } return EasyRefresh( controller: _refreshController, header: const ClassicHeader( dragText: '下拉刷新', armedText: '释放刷新', readyText: '刷新中...', processingText: '刷新中...', processedText: '刷新完成', failedText: '刷新失败', noMoreText: '没有更多数据', showMessage: false ), footer: ClassicFooter( dragText: '上拉加载', armedText: '释放加载', readyText: '加载中...', processingText: '加载中...', processedText: '加载完成', failedText: '加载失败', noMoreText: '没有更多数据', showMessage: false ), // 下拉刷新 onRefresh: () async { print('推荐列表下拉刷新被触发'); try { await controller.refreshRecommendData(); print( '推荐列表刷新完成, hasMore: $controller.recommendHasMore.value'); _refreshController.finishRefresh(); _refreshController.resetFooter(); } catch (e) { print('推荐列表刷新失败: $e'); _refreshController.finishRefresh(IndicatorResult.fail); } }, // 上拉加载更多 onLoad: () async { print('推荐列表上拉加载被触发, hasMore: $controller.recommendHasMore.value'); try { await controller.loadRecommendMoreData(); // 完成加载,根据是否有更多数据决定 if (controller.recommendHasMore.value) { _refreshController.finishLoad(IndicatorResult.success); print('推荐列表加载更多成功'); } else { _refreshController.finishLoad(IndicatorResult.noMore); print('推荐列表没有更多数据了'); } } catch (e) { print('推荐列表加载更多失败: $e'); _refreshController.finishLoad(IndicatorResult.fail); } }, child: SingleChildScrollView( child: Column( children: [ Container( padding: EdgeInsets.symmetric(horizontal: 12), margin: EdgeInsets.only(bottom: 10.w), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Container( width: 174.w, height: 126.w, decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(8.w)), color: Colors.white ), child: Column( children: [ Stack( children: [ ClipRRect( borderRadius: BorderRadius.all(Radius.circular(8.w)), child: CachedNetworkImage( imageUrl: "https://dating-agency-test.oss-accelerate.aliyuncs.com/1.png", width: 174.w, height: 84.w, fit: BoxFit.cover, ), ), Positioned( child: Container( width: 47.w, height: 17.w, decoration: BoxDecoration( borderRadius: BorderRadius.only( topLeft: Radius.circular(8.w), bottomRight: Radius.circular(8.w), ), color: const Color.fromRGBO(234, 57, 49, 1) ), child: Center( child: Text( "热门活动", style: TextStyle( fontSize: 9.w, color: Colors.white, fontWeight: FontWeight.w500 ), ), ), ), ) ], ), Container( padding: EdgeInsets.all(5.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "星河 DEEP--轻奢浪漫之旅", style: TextStyle( fontSize: 13.w, fontWeight: FontWeight.w500 ), ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "0人已报名", style: TextStyle( fontSize: 9.w, color: const Color.fromRGBO(144, 144, 144, 1), fontWeight: FontWeight.w500 ), ), Text( "查看详情", style: TextStyle( fontSize: 9.w, color: const Color.fromRGBO(144, 144, 144, 1), fontWeight: FontWeight.w500 ), ), ], ) ], ), ) ], ), ).onTap((){ Get.to(() => EventList()); }), Container( width: 167.w, height: 126.w, padding: EdgeInsets.all(7.w), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(8.w)), color: Colors.white ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "金牌红娘", style: TextStyle( fontSize: 13.w, fontWeight: FontWeight.w700 ), ), Text( "查看更多", style: TextStyle( fontSize: 9.w, color: const Color.fromRGBO(144, 144, 144, 1), fontWeight: FontWeight.w500 ), ).onTap((){ Get.to(() => MatchmakerPage()); }) ], ), Wrap( spacing: 14.w, runSpacing: 8.w, children: [ ...controller.matchmakerList.take(6).toList().map((e){ return MatchmakerItem(item: e); }) ], ) ], ), ), ], ), ), Container( padding: EdgeInsets.symmetric(horizontal: 12), margin: EdgeInsets.only(bottom: 10.w), height: 90.w, child: Swiper( autoplay: true, itemCount: controller.bannerLint.length, loop: true, onTap: (int index){ print(index); if(controller.bannerLint[index].jumpType == 2){ Get.to(() => OpenWebView(url: controller.bannerLint[index].jumpValue ?? "")); } else if(controller.bannerLint[index].jumpType == 3){ TDImageViewer.showImageViewer(context: context, images: [controller.bannerLint[index].jumpValue ?? ""]); } }, itemBuilder: (BuildContext context, int index) { return CachedNetworkImage( imageUrl: controller.bannerLint[index].image ?? "", height: 90.w, fit: BoxFit.cover, imageBuilder: (context, imageProvider) => Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), image: DecorationImage( image: imageProvider, fit: BoxFit.cover, ), ), ), errorWidget: (context, url, error) => Image.asset( Assets.imagesUserAvatar, width: 165.w, height: 165.w, fit: BoxFit.cover, ), ); }, ), ), ListView.separated( shrinkWrap: true, // ⭐ 关键 1:高度由内容决定 physics: const NeverScrollableScrollPhysics(), // ⭐ 关键 2:禁用自身滚动 // 关键:始终允许滚动,即使内容不足 // 移除顶部 padding,让刷新指示器可以正确显示在 AppBar 下方 padding: EdgeInsets.only(left: 12, right: 12), itemBuilder: (context, index) { // 空数据状态 if (controller.recommendFeed.isEmpty && index == 0) { // 使用足够的高度确保可以滚动 if (controller.recommendIsLoading.value) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('加载数据中...'), ], ), ); } else { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('暂无数据'), ], ), ); } } // 数据项 final item = controller.recommendFeed[index]; return ContentCard(item: item); }, separatorBuilder: (context, index) { // 空状态或加载状态时不显示分隔符 if (controller.recommendFeed.isEmpty) { return const SizedBox.shrink(); } return const SizedBox(height: 12); }, // 至少显示一个 item(用于显示加载或空状态) itemCount: controller.recommendFeed.isEmpty ? 1 : controller.recommendFeed.length, ) ], ), ) ); }); } @override bool get wantKeepAlive => true; }