diff --git a/assets/images/nearby_user.png b/assets/images/nearby_user.png new file mode 100644 index 0000000..3ca9fcc Binary files /dev/null and b/assets/images/nearby_user.png differ diff --git a/lib/controller/home/friend_footprint_controller.dart b/lib/controller/home/friend_footprint_controller.dart new file mode 100644 index 0000000..bc8c1e7 --- /dev/null +++ b/lib/controller/home/friend_footprint_controller.dart @@ -0,0 +1,6 @@ + +import 'package:get/get.dart'; + +class FriendFootprintController extends GetxController { + +} \ No newline at end of file diff --git a/lib/controller/home/home_controller.dart b/lib/controller/home/home_controller.dart index 93e838d..fafc259 100644 --- a/lib/controller/home/home_controller.dart +++ b/lib/controller/home/home_controller.dart @@ -15,7 +15,8 @@ class HomeController extends GetxController { final recommendFeed = [].obs; // 同城列表数据 final nearbyFeed = [].obs; - + final nearbyDisFeed = [].obs; + // 推荐列表的加载状态和分页信息 final recommendIsLoading = false.obs; final recommendPage = 1.obs; @@ -24,6 +25,7 @@ class HomeController extends GetxController { // 同城列表的加载状态和分页信息 final nearbyIsLoading = false.obs; final nearbyPage = 1.obs; + final nearbyDisPage = 1.obs; final nearbyHasMore = true.obs; // 当前标签页索引 @@ -38,6 +40,7 @@ class HomeController extends GetxController { final timelineTab = 0.obs; final RxnInt cityCode = RxnInt(); + final RxnInt districtCode = RxnInt(); final _locationPlugin = LocationPlugin(); String _status = 'Idle'; @@ -105,6 +108,10 @@ class HomeController extends GetxController { cityCode.value = ((_cityInfo?.code ?? 0) ~/ 100) * 100; + districtCode.value = _cityInfo?.code ?? 0; + + print(_cityInfo!.code); + print("_cityInfo.code"); } on CityInfoLookupException catch (e) { @@ -199,6 +206,42 @@ class HomeController extends GetxController { } } + /// 加载同城列表初始数据 + Future loadNearbyDisInitialData() async { + if(cityCode.value == null || cityCode.value == 0){ + await _fetchLocation(); + + } + if (nearbyIsLoading.value) return; + + try { + nearbyIsLoading.value = true; + nearbyPage.value = 1; + nearbyHasMore.value = true; + + // 获取同城数据 (type=1) + final result = await _fetchMarriageData( + pageNum: 1, + type: 2, + code: districtCode.value + ); + + // 重置并更新同城列表 + nearbyDisFeed.clear(); + nearbyDisFeed.addAll(result['records']); + + // 根据返回的分页信息判断是否还有更多数据 + final int currentPage = result['current'] ?? 1; + // final int totalPages = result['pages'] ?? 1; + final int totalPages = 100; + nearbyHasMore.value = currentPage < totalPages; + } catch (e) { + _handleError('获取同城列表异常', e, '同城列表加载失败,请稍后重试'); + } finally { + nearbyIsLoading.value = false; + } + } + /// 加载更多数据 Future loadMoreData([int? tabIndex]) async { final targetTab = tabIndex ?? selectedTabIndex.value; @@ -282,6 +325,41 @@ class HomeController extends GetxController { } } + /// 加载同城列表更多数据 + Future loadNearbyDisMoreData() async { + if (nearbyIsLoading.value || !nearbyHasMore.value) return; + + try { + nearbyIsLoading.value = true; + // final nextPage = nearbyPage.value + 1; + final nextPage = nearbyPage.value; + print('同城列表加载更多 - 当前页: ${nearbyPage.value}, 下一页: $nextPage'); + // 获取同城数据 (type=1) + final result = await _fetchMarriageData( + pageNum: nextPage, + type: 2, + code: districtCode.value + ); + + // 更新页码 + nearbyDisPage.value = nextPage; + + // 更新同城列表 + nearbyDisFeed.addAll(result['records']); + + // 根据返回的分页信息判断是否还有更多数据 + final int currentPage = result['current'] as int; + // final int totalPages = result['pages'] as int; + final int totalPages = 100; + nearbyHasMore.value = currentPage < totalPages; + print('同城列表加载更多完成 - 当前页: $currentPage, 总页数: $totalPages, 还有更多: ${nearbyHasMore.value}'); + } catch (e) { + _handleError('加载同城更多异常', e, '加载更多失败'); + } finally { + nearbyIsLoading.value = false; + } + } + /// 刷新数据 Future refreshData([int? tabIndex]) async { final targetTab = tabIndex ?? selectedTabIndex.value; @@ -358,6 +436,38 @@ class HomeController extends GetxController { } } + /// 刷新同城列表数据 + Future refreshNearbyDisData() async { + if (nearbyIsLoading.value) return; + + try { + nearbyIsLoading.value = true; + nearbyPage.value = 1; + nearbyHasMore.value = true; + + // 获取同城数据 (type=1) + final result = await _fetchMarriageData( + pageNum: 1, + type: 2, + code: districtCode.value + ); + + // 更新同城列表 + nearbyDisFeed.clear(); + nearbyDisFeed.addAll(result['records']); + + // 根据返回的分页信息判断是否还有更多数据 + final int currentPage = result['current'] ?? 1; + // final int totalPages = result['pages'] ?? 1; + final int totalPages = 100; + nearbyHasMore.value = currentPage < totalPages; + } catch (e) { + _handleError('刷新同城数据异常', e, '刷新失败,请稍后重试'); + } finally { + nearbyIsLoading.value = false; + } + } + /// 设置当前标签页 void setSelectedTabIndex(int index) async { print('Setting selected tab index to: $index'); @@ -391,7 +501,8 @@ class HomeController extends GetxController { pageNum: pageNum, pageSize: pageSize, type: type, - cityCode: type == 1 ? code : null + cityCode: type == 1 ? code : null, + districtCode: type == 2 ? code : null, ); if (response.data.isSuccess) { diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index 625b9d0..030d8da 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -173,6 +173,7 @@ class Assets { static const String imagesMore = 'assets/images/more.png'; static const String imagesMoreIcon = 'assets/images/more_icon.png'; static const String imagesMyWalletBg = 'assets/images/my_wallet_bg.png'; + static const String imagesNearbyUser = 'assets/images/nearby_user.png'; static const String imagesNoMoreTimeline = 'assets/images/no_more_timeline.png'; static const String imagesOnlineIcon = 'assets/images/online_icon.png'; static const String imagesOnlineMsgIcon = 'assets/images/online_msg_icon.png'; diff --git a/lib/network/api_urls.dart b/lib/network/api_urls.dart index 90d9d20..58931f7 100644 --- a/lib/network/api_urls.dart +++ b/lib/network/api_urls.dart @@ -106,6 +106,10 @@ class ApiUrls { 'dating-agency-service/user/del/photos'; static const String userPageLatestDatingRecord = 'dating-agency-chat-audio/user/page/latest/dating-record'; + static const String userGetFriendFootprintInfo = + 'dating-agency-chat-audio/user/get/friend-footprint-info'; + static const String userPageFriendFootprint = + '/dating-agency-chat-audio/user/page/friend-footprint'; //首页相关接口 static const String getMarriageList = diff --git a/lib/network/home_api.dart b/lib/network/home_api.dart index 1878158..18ee3b0 100644 --- a/lib/network/home_api.dart +++ b/lib/network/home_api.dart @@ -24,6 +24,7 @@ abstract class HomeApi { @Query('pageSize') required int pageSize, @Query('type') required int type, @Query('cityCode') int? cityCode, + @Query('districtCode') int? districtCode, }); @GET(ApiUrls.listMatchmakerTask) @@ -89,4 +90,13 @@ abstract class HomeApi { ); + @GET(ApiUrls.userGetFriendFootprintInfo) + Future>>> userGetFriendFootprintInfo(); + + @GET(ApiUrls.userPageFriendFootprint) + Future>>> userPageFriendFootprint({ + @Query('pageNum') required int pageNum, + @Query('pageSize') required int pageSize, + }); + } \ No newline at end of file diff --git a/lib/network/home_api.g.dart b/lib/network/home_api.g.dart index c66ab30..f7250ae 100644 --- a/lib/network/home_api.g.dart +++ b/lib/network/home_api.g.dart @@ -26,6 +26,7 @@ class _HomeApi implements HomeApi { required int pageSize, required int type, int? cityCode, + int? districtCode, }) async { final _extra = {}; final queryParameters = { @@ -33,6 +34,7 @@ class _HomeApi implements HomeApi { r'pageSize': pageSize, r'type': type, r'cityCode': cityCode, + r'districtCode': districtCode, }; queryParameters.removeWhere((k, v) => v == null); final _headers = {}; @@ -454,6 +456,81 @@ class _HomeApi implements HomeApi { return httpResponse; } + @override + Future>>> + userGetFriendFootprintInfo() async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType>>>( + Options(method: 'GET', headers: _headers, extra: _extra) + .compose( + _dio.options, + 'dating-agency-chat-audio/user/get/friend-footprint-info', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + final _result = await _dio.fetch>(_options); + late BaseResponse> _value; + try { + _value = BaseResponse>.fromJson( + _result.data!, + (json) => json is List + ? json.map((i) => i as String).toList() + : List.empty(), + ); + } on Object catch (e, s) { + errorLogger?.logError(e, s, _options); + rethrow; + } + final httpResponse = HttpResponse(_value, _result); + return httpResponse; + } + + @override + Future>>> + userPageFriendFootprint({required int pageNum, required int pageSize}) async { + final _extra = {}; + final queryParameters = { + r'pageNum': pageNum, + r'pageSize': pageSize, + }; + final _headers = {}; + const Map? _data = null; + final _options = + _setStreamType>>>( + Options(method: 'GET', headers: _headers, extra: _extra) + .compose( + _dio.options, + '/dating-agency-chat-audio/user/page/friend-footprint', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl), + ), + ); + final _result = await _dio.fetch>(_options); + late BaseResponse> _value; + try { + _value = BaseResponse>.fromJson( + _result.data!, + (json) => PaginatedResponse.fromJson( + json as Map, + (json) => json as dynamic, + ), + ); + } on Object catch (e, s) { + errorLogger?.logError(e, s, _options); + rethrow; + } + final httpResponse = HttpResponse(_value, _result); + return httpResponse; + } + RequestOptions _setStreamType(RequestOptions requestOptions) { if (T != dynamic && !(requestOptions.responseType == ResponseType.bytes || diff --git a/lib/pages/home/friend_footprint.dart b/lib/pages/home/friend_footprint.dart new file mode 100644 index 0000000..3412947 --- /dev/null +++ b/lib/pages/home/friend_footprint.dart @@ -0,0 +1,40 @@ +import 'package:dating_touchme_app/components/page_appbar.dart'; +import 'package:dating_touchme_app/controller/home/friend_footprint_controller.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'; + +class FriendFootprint extends StatelessWidget { + const FriendFootprint({super.key}); + + @override + Widget build(BuildContext context) { + return GetX( + init: FriendFootprintController(), + builder: (controller){ + return Scaffold( + appBar: PageAppbar(title: "好友脚印"), + body: Container( + padding: EdgeInsets.all(12.w), + child: EasyRefresh( + child: ListView.separated( + itemBuilder: (context, index){ + return Container(); + }, + separatorBuilder: (context, index) { + // 空状态或加载状态时不显示分隔符 + if (true) { + return const SizedBox.shrink(); + } + return const SizedBox(height: 12); + }, + itemCount: 10, + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/pages/home/recommend_tab.dart b/lib/pages/home/recommend_tab.dart index 3f617a1..d6af006 100644 --- a/lib/pages/home/recommend_tab.dart +++ b/lib/pages/home/recommend_tab.dart @@ -1,5 +1,7 @@ +import 'package:dating_touchme_app/generated/assets.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'; @@ -17,10 +19,17 @@ class _RecommendTabState extends State final HomeController controller = Get.find(); late final EasyRefreshController _refreshController; + final ScrollController _scrollController = ScrollController(); + + bool _shrink = false; + @override void initState() { super.initState(); _refreshController = EasyRefreshController(controlFinishRefresh: true, controlFinishLoad: true); + + + _scrollController.addListener(_onScroll); } @override @@ -29,6 +38,19 @@ class _RecommendTabState extends State super.dispose(); } + + void _onScroll() { + final offset = _scrollController.offset; + + final shouldShrink = offset > 200; + + if (shouldShrink != _shrink) { + setState(() { + _shrink = shouldShrink; + }); + } + } + @override Widget build(BuildContext context) { super.build(context); @@ -36,118 +58,177 @@ class _RecommendTabState extends State 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); + return Stack( + children: [ + Obx(() { + if (controller.recommendIsLoading.value && controller.recommendFeed.isEmpty) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircularProgressIndicator(), + SizedBox(height: 16), + Text('加载数据中...'), + ], + ), + ); } - }, - // 上拉加载更多 - 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: ListView.separated( - // 关键:始终允许滚动,即使内容不足 - // 移除顶部 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('暂无数据'), - ], - ), - ); + 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); } - } - // 数据项 - 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, - ) - ); - }); + }, + child: ListView.separated( + controller: _scrollController, + // 关键:始终允许滚动,即使内容不足 + // 移除顶部 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, + ) + ); + }), + Positioned( + right: 0, + bottom: 42, + child: Obx(() { + return AnimatedContainer( + duration: const Duration(milliseconds: 250), + width: _shrink ? 50 : 142, + height: 50, + padding: EdgeInsets.all(5), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(50)), + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Color(0xFF8359FF), + Color(0xFF3D8AE0), + ], + ), + ), + child: Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(40)), + child: Image.asset( + Assets.imagesUserAvatar, + width: 40, + height: 40, + ), + ), + if(!_shrink) SizedBox(width: 4,), + if(!_shrink) Column( + children: [ + Text( + "与你匹配的", + style: TextStyle( + fontSize: 15, + color: Colors.white + ), + ), + Text( + "${controller.cityCode.value ?? 0}人正在连麦...", + style: TextStyle( + fontSize: 11, + color: Colors.white + ), + ) + ], + ) + ], + ), + ); + }), + ) + ], + ); } @override diff --git a/lib/pages/home/report_page.dart b/lib/pages/home/report_page.dart index 56cc797..f174a30 100644 --- a/lib/pages/home/report_page.dart +++ b/lib/pages/home/report_page.dart @@ -142,7 +142,7 @@ class ReportPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "资料作假", + "骚扰谩骂低俗", style: TextStyle( fontSize: 12.w, color: const Color.fromRGBO(51, 51, 51, 1), @@ -169,7 +169,7 @@ class ReportPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "色情低俗", + "垃圾广告", style: TextStyle( fontSize: 13.w, color: const Color.fromRGBO(51, 51, 51, 1), @@ -196,7 +196,7 @@ class ReportPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "涉政/涉独", + "涉政涉恐", style: TextStyle( fontSize: 13.w, color: const Color.fromRGBO(51, 51, 51, 1), @@ -223,7 +223,7 @@ class ReportPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "违法违禁", + "欺诈骗钱", style: TextStyle( fontSize: 13.w, color: const Color.fromRGBO(51, 51, 51, 1), @@ -250,7 +250,7 @@ class ReportPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "未成年相关", + "影响相亲体验", style: TextStyle( fontSize: 13.w, color: const Color.fromRGBO(51, 51, 51, 1), @@ -277,7 +277,7 @@ class ReportPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "欺诈/广告/引导第三方交易", + "涉黄", style: TextStyle( fontSize: 13.w, color: const Color.fromRGBO(51, 51, 51, 1), @@ -298,60 +298,6 @@ class ReportPage extends StatelessWidget { ], ), ), - SizedBox( - height: 32.w, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "恶意骚扰/侮辱谩骂", - style: TextStyle( - fontSize: 13.w, - color: const Color.fromRGBO(51, 51, 51, 1), - fontWeight: FontWeight.w500 - ), - ), - Checkbox( - value: controller.checked.value == 7, - onChanged: (value) { - controller.checked.value = 7; - - }, - activeColor: const Color.fromRGBO(117, 98, 249, 1), - side: const BorderSide(color: Colors.grey), - shape: const CircleBorder(), - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - ], - ), - ), - SizedBox( - height: 32.w, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "其他", - style: TextStyle( - fontSize: 13.w, - color: const Color.fromRGBO(51, 51, 51, 1), - fontWeight: FontWeight.w500 - ), - ), - Checkbox( - value: controller.checked.value == 8, - onChanged: (value) { - controller.checked.value = 8; - - }, - activeColor: const Color.fromRGBO(117, 98, 249, 1), - side: const BorderSide(color: Colors.grey), - shape: const CircleBorder(), - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - ], - ), - ), SizedBox(height: 29.w ,), Row( mainAxisAlignment: MainAxisAlignment.start, diff --git a/lib/pages/message/conversation_tab.dart b/lib/pages/message/conversation_tab.dart index 04e80b4..1e67151 100644 --- a/lib/pages/message/conversation_tab.dart +++ b/lib/pages/message/conversation_tab.dart @@ -2,6 +2,7 @@ import 'package:dating_touchme_app/extension/ex_widget.dart'; import 'package:dating_touchme_app/im/im_manager.dart'; import 'package:dating_touchme_app/pages/message/chat_page.dart'; import 'package:dating_touchme_app/pages/message/connect_history_page.dart'; +import 'package:dating_touchme_app/pages/message/nearby_user.dart'; import 'package:flutter/material.dart'; import 'package:dating_touchme_app/generated/assets.dart'; import 'package:get/get.dart'; @@ -37,6 +38,36 @@ class _ConversationTabState extends State ), child: Row( children: [ + Column( + children: [ + Container( + width: 90.w, + height: 50.w, + margin: EdgeInsets.only(bottom: 5.w), + decoration: BoxDecoration( + color: const Color.fromRGBO(255, 228, 222, 1), + borderRadius: BorderRadius.all(Radius.circular(50.w)) + ), + child: Center( + child: Image.asset( + gaplessPlayback: true, + Assets.imagesNearbyUser, + width: 40.w, + height: 40.w, + ), + ), + ), + Text( + "附近的人", + style: TextStyle( + fontSize: 11.w + ), + ) + ], + ).onTap((){ + Get.to(() => NearbyUser()); + }), + SizedBox(width: 30.w), Column( children: [ Container( @@ -65,7 +96,7 @@ class _ConversationTabState extends State ], ).onTap((){ Get.to(() => ConnectHistoryPage()); - }) + }), ], ), ); diff --git a/lib/pages/message/nearby_user.dart b/lib/pages/message/nearby_user.dart new file mode 100644 index 0000000..af63fb7 --- /dev/null +++ b/lib/pages/message/nearby_user.dart @@ -0,0 +1,232 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dating_touchme_app/components/page_appbar.dart'; +import 'package:dating_touchme_app/controller/home/home_controller.dart'; +import 'package:dating_touchme_app/extension/ex_widget.dart'; +import 'package:dating_touchme_app/generated/assets.dart'; +import 'package:dating_touchme_app/model/home/marriage_data.dart'; +import 'package:dating_touchme_app/pages/home/user_information_page.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'; + +class NearbyUser extends StatefulWidget { + const NearbyUser({super.key}); + + @override + State createState() => _NearbyUserState(); +} + +class _NearbyUserState extends State { + + + + final HomeController controller = Get.put(HomeController()); + late final EasyRefreshController _refreshController; + + @override + void initState() { + super.initState(); + _refreshController = EasyRefreshController(controlFinishRefresh: true, controlFinishLoad: true); + + controller.loadNearbyDisInitialData(); + + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PageAppbar(title: "附近的异性"), + body: Obx(() { + if (controller.nearbyIsLoading.value && controller.nearbyDisFeed.isEmpty) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircularProgressIndicator(), + SizedBox(height: 16), + Text('加载附近的人数据中...'), + ], + ), + ); + } + + if (controller.nearbyDisFeed.isEmpty) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.people_outline, size: 80, color: Colors.grey[300]), + SizedBox(height: 16), + Text( + '暂无附近的人,点击刷新', + style: TextStyle(fontSize: 14, color: Colors.grey), + ), + SizedBox(height: 8), + ], + ), + ).onTap((){ + controller.loadNearbyDisInitialData(); + }); + } + + return EasyRefresh( + controller: _refreshController, + header: ClassicHeader( + dragText: '下拉刷新', + armedText: '释放刷新', + readyText: '刷新中...', + processingText: '刷新中...', + processedText: '刷新完成', + failedText: '刷新失败', + noMoreText: '没有更多数据', + messageText: '更新时间 %T', + showMessage: false + ), + footer: ClassicFooter( + dragText: '上拉加载', + armedText: '释放加载', + readyText: '加载中...', + processingText: '加载中...', + processedText: '加载完成', + failedText: '加载失败', + noMoreText: '没有更多数据', + showMessage: false, + ), + + // 下拉刷新 + onRefresh: () async { + print('推荐列表下拉刷新被触发'); + try { + await controller.refreshNearbyDisData(); + 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.loadNearbyDisMoreData(); + // 完成加载,根据是否有更多数据决定 + if (controller.recommendHasMore.value) { + _refreshController.finishLoad(IndicatorResult.success); + print('推荐列表加载更多成功'); + } else { + _refreshController.finishLoad(IndicatorResult.noMore); + print('推荐列表没有更多数据了'); + } + } catch (e) { + print('推荐列表加载更多失败: $e'); + _refreshController.finishLoad(IndicatorResult.fail); + } + }, + child: GridView.builder( + padding: EdgeInsets.only(top: 8.w, left: 10.w, right: 8.w, bottom: 0.w), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, // 每行2个 + crossAxisSpacing: 4.w, // 左右间距 + mainAxisSpacing: 4.w, // 上下间距 + childAspectRatio: 1, // 宽高比 + ), + itemCount: controller.nearbyDisFeed.isEmpty ? 1 : controller.nearbyDisFeed.length, + itemBuilder: (context, index) { + final visitor = controller.nearbyDisFeed[index]; + return VisitorItem(visitor: visitor); + }, + ), + ); + }), + ); + } +} + + +class VisitorItem extends StatelessWidget { + final MarriageData visitor; + + const VisitorItem({Key? key, required this.visitor}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(10.w)), + child: CachedNetworkImage( + imageUrl: "${visitor.profilePhoto}?x-oss-process=image/format,webp/resize,w_240", + width: 113.w, + height: 113.w, + fit: BoxFit.cover, + placeholder: (context, url) => Container( + color: Colors.white38, + child: Center( + child: CircularProgressIndicator( + strokeWidth: 1.w, + color: Colors.grey, + ), + ), + ), + errorWidget: (context, url, error) => + Image.asset( + Assets.imagesUserAvatar, + width: 113.w, + height: 113.w, + fit: BoxFit.cover, + ), + ), + ), + Positioned( + left: 8.w, + bottom: 12.w, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 113.w, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "${visitor.age}岁 | ${visitor.districtName}", + style: TextStyle( + fontSize: 8.w, + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ) + ], + ), + ) + ], + ).onTap((){ + Get.to(() => UserInformationPage(miId: visitor.miId)); + }); + } + +// String _formatTime(String timestamp) { +// var time = DateTime.parse(timestamp); +// final now = DateTime.now(); +// final difference = now.difference(time); +// +// if (difference.inMinutes < 1) { +// return '刚刚'; +// } else if (difference.inHours < 1) { +// return '${difference.inMinutes}分钟前'; +// } else if (difference.inDays < 1) { +// return '${difference.inHours}小时前'; +// } else if (difference.inDays < 7) { +// return '${difference.inDays}天前'; +// } else { +// return '${time.month}/${time.day}'; +// } +// } + +} \ No newline at end of file