16 changed files with 716 additions and 146 deletions
Unified View
Diff Options
-
309lib/controller/home/home_controller.dart
-
6lib/controller/setting/blacklist_controller.dart
-
18lib/model/home/check_black_data.dart
-
15lib/model/home/friend_footprint_info_data.dart
-
92lib/model/home/friend_footprint_list_data.dart
-
4lib/network/api_urls.dart
-
4lib/network/home_api.dart
-
36lib/network/home_api.g.dart
-
6lib/network/user_api.dart
-
33lib/network/user_api.g.dart
-
34lib/pages/home/content_card.dart
-
153lib/pages/home/friend_footprint_page.dart
-
120lib/pages/home/recommend_tab.dart
-
26lib/pages/mine/mine_page.dart
-
2lib/pages/mine/real_feedback_page.dart
-
4lib/pages/setting/blacklist_page.dart
@ -0,0 +1,18 @@ |
|||||
|
class CheckBlackData { |
||||
|
bool? activeBlack; |
||||
|
bool? passiveBlack; |
||||
|
|
||||
|
CheckBlackData({this.activeBlack, this.passiveBlack}); |
||||
|
|
||||
|
CheckBlackData.fromJson(Map<String, dynamic> json) { |
||||
|
activeBlack = json['activeBlack']; |
||||
|
passiveBlack = json['passiveBlack']; |
||||
|
} |
||||
|
|
||||
|
Map<String, dynamic> toJson() { |
||||
|
final Map<String, dynamic> data = new Map<String, dynamic>(); |
||||
|
data['activeBlack'] = this.activeBlack; |
||||
|
data['passiveBlack'] = this.passiveBlack; |
||||
|
return data; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
class FriendFootprintInfoData { |
||||
|
List<String>? profilePhotoList; |
||||
|
|
||||
|
FriendFootprintInfoData({this.profilePhotoList}); |
||||
|
|
||||
|
FriendFootprintInfoData.fromJson(Map<String, dynamic> json) { |
||||
|
profilePhotoList = json['profilePhotoList'].cast<String>(); |
||||
|
} |
||||
|
|
||||
|
Map<String, dynamic> toJson() { |
||||
|
final Map<String, dynamic> data = new Map<String, dynamic>(); |
||||
|
data['profilePhotoList'] = this.profilePhotoList; |
||||
|
return data; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,92 @@ |
|||||
|
class FriendFootprintListData { |
||||
|
List<Records>? records; |
||||
|
int? total; |
||||
|
int? size; |
||||
|
int? current; |
||||
|
int? pages; |
||||
|
|
||||
|
FriendFootprintListData( |
||||
|
{this.records, this.total, this.size, this.current, this.pages}); |
||||
|
|
||||
|
FriendFootprintListData.fromJson(Map<String, dynamic> json) { |
||||
|
if (json['records'] != null) { |
||||
|
records = <Records>[]; |
||||
|
json['records'].forEach((v) { |
||||
|
records!.add(new Records.fromJson(v)); |
||||
|
}); |
||||
|
} |
||||
|
total = json['total']; |
||||
|
size = json['size']; |
||||
|
current = json['current']; |
||||
|
pages = json['pages']; |
||||
|
} |
||||
|
|
||||
|
Map<String, dynamic> toJson() { |
||||
|
final Map<String, dynamic> data = new Map<String, dynamic>(); |
||||
|
if (this.records != null) { |
||||
|
data['records'] = this.records!.map((v) => v.toJson()).toList(); |
||||
|
} |
||||
|
data['total'] = this.total; |
||||
|
data['size'] = this.size; |
||||
|
data['current'] = this.current; |
||||
|
data['pages'] = this.pages; |
||||
|
return data; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class Records { |
||||
|
String? channelId; |
||||
|
String? nickName; |
||||
|
String? profilePhoto; |
||||
|
int? genderCode; |
||||
|
String? birthYear; |
||||
|
String? birthDate; |
||||
|
int? age; |
||||
|
int? provinceCode; |
||||
|
String? provinceName; |
||||
|
int? cityCode; |
||||
|
String? cityName; |
||||
|
|
||||
|
Records( |
||||
|
{this.channelId, |
||||
|
this.nickName, |
||||
|
this.profilePhoto, |
||||
|
this.genderCode, |
||||
|
this.birthYear, |
||||
|
this.birthDate, |
||||
|
this.age, |
||||
|
this.provinceCode, |
||||
|
this.provinceName, |
||||
|
this.cityCode, |
||||
|
this.cityName}); |
||||
|
|
||||
|
Records.fromJson(Map<String, dynamic> json) { |
||||
|
channelId = json['channelId']; |
||||
|
nickName = json['nickName']; |
||||
|
profilePhoto = json['profilePhoto']; |
||||
|
genderCode = json['genderCode']; |
||||
|
birthYear = json['birthYear']; |
||||
|
birthDate = json['birthDate']; |
||||
|
age = json['age']; |
||||
|
provinceCode = json['provinceCode']; |
||||
|
provinceName = json['provinceName']; |
||||
|
cityCode = json['cityCode']; |
||||
|
cityName = json['cityName']; |
||||
|
} |
||||
|
|
||||
|
Map<String, dynamic> toJson() { |
||||
|
final Map<String, dynamic> data = new Map<String, dynamic>(); |
||||
|
data['channelId'] = this.channelId; |
||||
|
data['nickName'] = this.nickName; |
||||
|
data['profilePhoto'] = this.profilePhoto; |
||||
|
data['genderCode'] = this.genderCode; |
||||
|
data['birthYear'] = this.birthYear; |
||||
|
data['birthDate'] = this.birthDate; |
||||
|
data['age'] = this.age; |
||||
|
data['provinceCode'] = this.provinceCode; |
||||
|
data['provinceName'] = this.provinceName; |
||||
|
data['cityCode'] = this.cityCode; |
||||
|
data['cityName'] = this.cityName; |
||||
|
return data; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,153 @@ |
|||||
|
import 'package:dating_touchme_app/components/page_appbar.dart'; |
||||
|
import 'package:dating_touchme_app/controller/home/home_controller.dart'; |
||||
|
import 'package:dating_touchme_app/pages/home/content_card.dart'; |
||||
|
import 'package:easy_refresh/easy_refresh.dart'; |
||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:get/get.dart'; |
||||
|
|
||||
|
class FriendFootprintPage extends StatefulWidget { |
||||
|
const FriendFootprintPage({super.key}); |
||||
|
|
||||
|
@override |
||||
|
State<FriendFootprintPage> createState() => _FriendFootprintPageState(); |
||||
|
} |
||||
|
|
||||
|
class _FriendFootprintPageState extends State<FriendFootprintPage> { |
||||
|
final HomeController controller = Get.find<HomeController>(); |
||||
|
late final EasyRefreshController _refreshController; |
||||
|
|
||||
|
final ScrollController _scrollController = ScrollController(); |
||||
|
|
||||
|
@override |
||||
|
void initState() { |
||||
|
super.initState(); |
||||
|
_refreshController = EasyRefreshController(controlFinishRefresh: true, controlFinishLoad: true); |
||||
|
controller.loadFriendFootInitialData(); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
void dispose() { |
||||
|
_refreshController.dispose(); |
||||
|
super.dispose(); |
||||
|
} |
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return Scaffold( |
||||
|
appBar: PageAppbar(title: "好友足迹"), |
||||
|
body: 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.refreshFriendData(); |
||||
|
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.loadFriendFootMoreData(); |
||||
|
// 完成加载,根据是否有更多数据决定 |
||||
|
if (controller.friendHasMore.value) { |
||||
|
_refreshController.finishLoad(IndicatorResult.success); |
||||
|
print('推荐列表加载更多成功'); |
||||
|
} else { |
||||
|
_refreshController.finishLoad(IndicatorResult.noMore); |
||||
|
print('推荐列表没有更多数据了'); |
||||
|
} |
||||
|
} catch (e) { |
||||
|
print('推荐列表加载更多失败: $e'); |
||||
|
_refreshController.finishLoad(IndicatorResult.fail); |
||||
|
} |
||||
|
}, |
||||
|
child: ListView.separated( |
||||
|
controller: _scrollController, |
||||
|
// 关键:始终允许滚动,即使内容不足 |
||||
|
// 移除顶部 padding,让刷新指示器可以正确显示在 AppBar 下方 |
||||
|
padding: EdgeInsets.only(left: 12, right: 12), |
||||
|
itemBuilder: (context, index) { |
||||
|
// 空数据状态 |
||||
|
if (controller.friendFootFeed.isEmpty && index == 0) { |
||||
|
// 使用足够的高度确保可以滚动 |
||||
|
if (controller.friendIsLoading.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.friendFootFeed[index]; |
||||
|
return ContentCard(item: item, isFoot: true,); |
||||
|
}, |
||||
|
separatorBuilder: (context, index) { |
||||
|
// 空状态或加载状态时不显示分隔符 |
||||
|
if (controller.friendFootFeed.isEmpty) { |
||||
|
return const SizedBox.shrink(); |
||||
|
} |
||||
|
return const SizedBox(height: 12); |
||||
|
}, |
||||
|
// 至少显示一个 item(用于显示加载或空状态) |
||||
|
itemCount: controller.friendFootFeed.isEmpty ? 1 : controller.friendFootFeed.length, |
||||
|
) |
||||
|
); |
||||
|
}), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save