Browse Source

完善页面

dev-2.0
王子贤 1 day ago
parent
commit
e0e4bbc53f
12 changed files with 712 additions and 173 deletions
  1. BIN
      assets/images/nearby_user.png
  2. 6
      lib/controller/home/friend_footprint_controller.dart
  3. 115
      lib/controller/home/home_controller.dart
  4. 1
      lib/generated/assets.dart
  5. 4
      lib/network/api_urls.dart
  6. 10
      lib/network/home_api.dart
  7. 77
      lib/network/home_api.g.dart
  8. 40
      lib/pages/home/friend_footprint.dart
  9. 301
      lib/pages/home/recommend_tab.dart
  10. 66
      lib/pages/home/report_page.dart
  11. 33
      lib/pages/message/conversation_tab.dart
  12. 232
      lib/pages/message/nearby_user.dart

BIN
assets/images/nearby_user.png

Before After
Width: 160  |  Height: 160  |  Size: 23 KiB

6
lib/controller/home/friend_footprint_controller.dart

@ -0,0 +1,6 @@
import 'package:get/get.dart';
class FriendFootprintController extends GetxController {
}

115
lib/controller/home/home_controller.dart

@ -15,7 +15,8 @@ class HomeController extends GetxController {
final recommendFeed = <MarriageData>[].obs;
//
final nearbyFeed = <MarriageData>[].obs;
final nearbyDisFeed = <MarriageData>[].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<void> 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<void> loadMoreData([int? tabIndex]) async {
final targetTab = tabIndex ?? selectedTabIndex.value;
@ -282,6 +325,41 @@ class HomeController extends GetxController {
}
}
///
Future<void> 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<void> refreshData([int? tabIndex]) async {
final targetTab = tabIndex ?? selectedTabIndex.value;
@ -358,6 +436,38 @@ class HomeController extends GetxController {
}
}
///
Future<void> 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) {

1
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';

4
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 =

10
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<HttpResponse<BaseResponse<List<String>>>> userGetFriendFootprintInfo();
@GET(ApiUrls.userPageFriendFootprint)
Future<HttpResponse<BaseResponse<PaginatedResponse<dynamic>>>> userPageFriendFootprint({
@Query('pageNum') required int pageNum,
@Query('pageSize') required int pageSize,
});
}

77
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 = <String, dynamic>{};
final queryParameters = <String, dynamic>{
@ -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 = <String, dynamic>{};
@ -454,6 +456,81 @@ class _HomeApi implements HomeApi {
return httpResponse;
}
@override
Future<HttpResponse<BaseResponse<List<String>>>>
userGetFriendFootprintInfo() async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<HttpResponse<BaseResponse<List<String>>>>(
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<Map<String, dynamic>>(_options);
late BaseResponse<List<String>> _value;
try {
_value = BaseResponse<List<String>>.fromJson(
_result.data!,
(json) => json is List<dynamic>
? json.map<String>((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<HttpResponse<BaseResponse<PaginatedResponse<dynamic>>>>
userPageFriendFootprint({required int pageNum, required int pageSize}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{
r'pageNum': pageNum,
r'pageSize': pageSize,
};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options =
_setStreamType<HttpResponse<BaseResponse<PaginatedResponse<dynamic>>>>(
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<Map<String, dynamic>>(_options);
late BaseResponse<PaginatedResponse<dynamic>> _value;
try {
_value = BaseResponse<PaginatedResponse<dynamic>>.fromJson(
_result.data!,
(json) => PaginatedResponse<dynamic>.fromJson(
json as Map<String, dynamic>,
(json) => json as dynamic,
),
);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
final httpResponse = HttpResponse(_value, _result);
return httpResponse;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||

40
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<FriendFootprintController>(
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,
),
),
),
);
},
);
}
}

301
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<RecommendTab>
final HomeController controller = Get.find<HomeController>();
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<RecommendTab>
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<RecommendTab>
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

66
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,

33
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<ConversationTab>
),
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<ConversationTab>
],
).onTap((){
Get.to(() => ConnectHistoryPage());
})
}),
],
),
);

232
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<NearbyUser> createState() => _NearbyUserState();
}
class _NearbyUserState extends State<NearbyUser> {
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}';
// }
// }
}
Loading…
Cancel
Save