575 lines
16 KiB
Dart
575 lines
16 KiB
Dart
import '../../apis/app.dart';
|
||
import 'package:flutter/material.dart';
|
||
|
||
import '../../components/CustomAlertDialog.dart';
|
||
import '../../components/emptyState.dart';
|
||
|
||
// 创建数据模型
|
||
class FollowData {
|
||
final int userId;
|
||
final String nickName;
|
||
final String avatar;
|
||
final String? detail;
|
||
final DateTime createTime;
|
||
final String followStatus;
|
||
|
||
FollowData({
|
||
required this.userId,
|
||
required this.nickName,
|
||
required this.avatar,
|
||
this.detail,
|
||
required this.createTime,
|
||
required this.followStatus,
|
||
});
|
||
|
||
factory FollowData.fromJson(Map<String, dynamic> json) {
|
||
return FollowData(
|
||
userId: json['userId'] ?? 0,
|
||
nickName: json['nickName'] ?? '',
|
||
avatar: json['avatar'] ?? '',
|
||
detail: json['detail'],
|
||
createTime: json['createTime'] != null
|
||
? DateTime.parse(json['createTime'])
|
||
: DateTime.now(),
|
||
followStatus: json['followStatus'] ?? '0',
|
||
);
|
||
}
|
||
}
|
||
|
||
class LikePage extends StatefulWidget {
|
||
final Map<String, int> initialTabIndex;
|
||
|
||
const LikePage({super.key, required this.initialTabIndex});
|
||
|
||
@override
|
||
State<LikePage> createState() => _LikePageState();
|
||
}
|
||
|
||
class _LikePageState extends State<LikePage> with SingleTickerProviderStateMixin {
|
||
List<FollowData> followList = [];
|
||
List<FollowData> fansList = [];
|
||
// 为关注列表和粉丝列表分别添加总数计数
|
||
int followTotalCount = 0;
|
||
int fansTotalCount = 0;
|
||
late TabController _tabController;
|
||
bool isLoading = false;
|
||
int currentPage = 1;
|
||
int totalCount = 0;
|
||
final ScrollController _scrollController = ScrollController();
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_tabController = TabController(
|
||
length: 2,
|
||
vsync: this,
|
||
initialIndex: widget.initialTabIndex["initialTabIndex"] ?? 0, // 设置初始索引
|
||
);
|
||
_tabController.addListener(_onTabChanged);
|
||
_scrollController.addListener(_onScroll);
|
||
_initializeData();
|
||
}
|
||
|
||
void _initializeData() {
|
||
// 使用 widget.initialTabIndex 来决定加载哪个列表
|
||
if (widget.initialTabIndex["initialTabIndex"] == 1) {
|
||
getMyFans();
|
||
} else {
|
||
getMyFollow();
|
||
}
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_scrollController.dispose();
|
||
_tabController.dispose();
|
||
super.dispose();
|
||
}
|
||
|
||
@override
|
||
void didChangeDependencies() {
|
||
super.didChangeDependencies();
|
||
_tabController.index = widget.initialTabIndex["initialTabIndex"] ?? 0;
|
||
}
|
||
|
||
void _onScroll() {
|
||
if (_scrollController.position.pixels ==
|
||
_scrollController.position.maxScrollExtent) {
|
||
_loadMore();
|
||
}
|
||
}
|
||
|
||
Future<void> _loadMore() async {
|
||
if (isLoading) return;
|
||
|
||
final currentList = _tabController.index == 0 ? followList : fansList;
|
||
final totalItems = _tabController.index == 0 ? followTotalCount : fansTotalCount;
|
||
|
||
if (currentList.length >= totalItems) return;
|
||
|
||
if (_tabController.index == 0) {
|
||
await getMyFollow(page: currentPage + 1);
|
||
} else {
|
||
await getMyFans(page: currentPage + 1);
|
||
}
|
||
}
|
||
|
||
void _onTabChanged() {
|
||
if (_tabController.indexIsChanging) {
|
||
setState(() {
|
||
currentPage = 1;
|
||
});
|
||
if (_tabController.index == 0) {
|
||
getMyFollow(page: 1);
|
||
} else {
|
||
getMyFans(page: 1);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 添加刷新列表的方法
|
||
void refreshList() {
|
||
setState(() {
|
||
if (_tabController.index == 0) {
|
||
followList = [];
|
||
} else {
|
||
fansList = [];
|
||
}
|
||
currentPage = 1;
|
||
});
|
||
if (_tabController.index == 0) {
|
||
getMyFollow(page: 1);
|
||
} else {
|
||
getMyFans(page: 1);
|
||
}
|
||
}
|
||
|
||
Future<void> getMyFans({int page = 1}) async {
|
||
if (isLoading) return;
|
||
|
||
setState(() => isLoading = true);
|
||
try {
|
||
final result = await userApi.getMyFans(
|
||
page: page,
|
||
pageSize: 10,
|
||
);
|
||
|
||
if (result['code'] == 200) {
|
||
final List<dynamic> data = result['rows'];
|
||
setState(() {
|
||
if (page == 1) {
|
||
fansList = data.map((item) => FollowData.fromJson(item)).toList();
|
||
} else {
|
||
fansList.addAll(data.map((item) => FollowData.fromJson(item)).toList());
|
||
}
|
||
fansTotalCount = result['total']; // 更新粉丝总数
|
||
currentPage = page;
|
||
});
|
||
}
|
||
} catch (e) {
|
||
if (mounted) {
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
const SnackBar(content: Text('获取粉丝列表失败')),
|
||
);
|
||
}
|
||
} finally {
|
||
if (mounted) {
|
||
setState(() => isLoading = false);
|
||
}
|
||
}
|
||
}
|
||
|
||
Future<void> getMyFollow({int page = 1}) async {
|
||
if (isLoading) return;
|
||
|
||
setState(() => isLoading = true);
|
||
try {
|
||
final result = await userApi.getMyFollow(
|
||
page: page,
|
||
pageSize: 10,
|
||
);
|
||
|
||
if (result['code'] == 200) {
|
||
final List<dynamic> data = result['rows'];
|
||
setState(() {
|
||
if (page == 1) {
|
||
followList = data.map((item) => FollowData.fromJson(item)).toList();
|
||
} else {
|
||
followList.addAll(data.map((item) => FollowData.fromJson(item)).toList());
|
||
}
|
||
followTotalCount = result['total']; // 更新关注总数
|
||
currentPage = page;
|
||
});
|
||
}
|
||
} catch (e) {
|
||
if (mounted) {
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
const SnackBar(content: Text('获取关注列表失败')),
|
||
);
|
||
}
|
||
} finally {
|
||
if (mounted) {
|
||
setState(() => isLoading = false);
|
||
}
|
||
}
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return DefaultTabController(
|
||
initialIndex: widget.initialTabIndex["initialTabIndex"] ?? 0,
|
||
length: 2,
|
||
child: Scaffold(
|
||
appBar: AppBar(
|
||
leading: Padding(
|
||
padding: const EdgeInsets.all(8.0),
|
||
child: IconButton(
|
||
icon: Image.asset(
|
||
'images/arraw.png',
|
||
width: 11,
|
||
height:22,
|
||
fit: BoxFit.cover,
|
||
),
|
||
onPressed: () => Navigator.pop(context),
|
||
),
|
||
),
|
||
title: Center( // 将TabBar居中
|
||
child: SizedBox(
|
||
width: 316, // 设置一个合适的宽度
|
||
child: TabBar(
|
||
controller: _tabController,
|
||
tabs: const [
|
||
Tab(text: '关注'),
|
||
Tab(text: '欢迎度'),
|
||
],
|
||
indicator: const UnderlineTabIndicator(
|
||
borderSide: BorderSide(
|
||
width: 2.0,
|
||
color: Color(0xFF80DAA4),
|
||
),
|
||
insets: EdgeInsets.symmetric(horizontal: 30.0),
|
||
),
|
||
labelColor: Colors.white,
|
||
unselectedLabelColor: Colors.grey,
|
||
),
|
||
),
|
||
),
|
||
// 添加一个空的actions保持标题居中
|
||
actions: [
|
||
const SizedBox(width: 48), // 与leading宽度相同
|
||
],
|
||
),
|
||
body: Container(
|
||
decoration: const BoxDecoration(color: Color(0xff0C0C16)),
|
||
child: RefreshIndicator(
|
||
onRefresh: () async {
|
||
refreshList();
|
||
},
|
||
child: TabBarView(
|
||
controller: _tabController,
|
||
children: [
|
||
LikeTab(
|
||
followList: followList,
|
||
scrollController: _scrollController,
|
||
isLoading: isLoading,
|
||
totalCount: followTotalCount,
|
||
onRefreshNeeded: refreshList,
|
||
isFollowTab: true,
|
||
),
|
||
LikeTab(
|
||
followList: fansList,
|
||
scrollController: _scrollController,
|
||
isLoading: isLoading,
|
||
totalCount: fansTotalCount,
|
||
onRefreshNeeded: refreshList,
|
||
isFollowTab: false,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
class LikeTab extends StatelessWidget {
|
||
final List<FollowData> followList;
|
||
final ScrollController scrollController;
|
||
final bool isLoading;
|
||
final int totalCount;
|
||
final VoidCallback onRefreshNeeded;
|
||
final bool isFollowTab;
|
||
|
||
const LikeTab({
|
||
super.key,
|
||
required this.followList,
|
||
required this.scrollController,
|
||
required this.isLoading,
|
||
required this.totalCount,
|
||
required this.onRefreshNeeded,
|
||
required this.isFollowTab,
|
||
});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
// 如果列表为空,直接显示占满屏幕的空状态
|
||
if (followList.isEmpty) {
|
||
return Container(
|
||
width: double.infinity,
|
||
height: double.infinity,
|
||
decoration: const BoxDecoration(
|
||
image: DecorationImage(
|
||
image: AssetImage('images/mine_bg.png'),
|
||
fit: BoxFit.cover,
|
||
),
|
||
),
|
||
child: const Center(
|
||
child: EmptyState(message: '暂无数据'),
|
||
),
|
||
);
|
||
}
|
||
return Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(20, 10, 20, 10),
|
||
child: Text(
|
||
isFollowTab ? '我的关注($totalCount)' : '我的欢迎度($totalCount)',
|
||
style: const TextStyle(fontSize: 12, color: Colors.white),
|
||
textAlign: TextAlign.left,
|
||
),
|
||
),
|
||
Expanded(
|
||
child: ListView.builder(
|
||
controller: scrollController,
|
||
padding: const EdgeInsets.symmetric(horizontal: 20), // 统一左边距
|
||
itemCount: followList.length + 1,
|
||
itemBuilder: (context, index) {
|
||
if (index == followList.length) {
|
||
return Container(
|
||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||
alignment: Alignment.center,
|
||
child: followList.isEmpty
|
||
? const EmptyState(message: '暂无数据')
|
||
: followList.length < totalCount
|
||
? const SizedBox(
|
||
width: 24,
|
||
height: 24,
|
||
child: CircularProgressIndicator(
|
||
strokeWidth: 2.0,
|
||
color: Colors.white,
|
||
),
|
||
)
|
||
: const Text(
|
||
'已经到底啦',
|
||
style: TextStyle(
|
||
color: Colors.grey,
|
||
fontSize: 14,
|
||
),
|
||
),
|
||
);
|
||
}
|
||
final item = followList[index];
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(vertical: 18),
|
||
child: FollowItem(
|
||
userId: item.userId,
|
||
nickName: item.nickName,
|
||
detail: item.detail ?? '',
|
||
followStatus: item.followStatus,
|
||
avatar: item.avatar,
|
||
isFollowTab: isFollowTab,
|
||
onFollowStatusChanged: onRefreshNeeded,
|
||
),
|
||
);
|
||
},
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
}
|
||
|
||
class FollowItem extends StatefulWidget {
|
||
final String avatar;
|
||
final String nickName;
|
||
final String detail;
|
||
final String followStatus;
|
||
final int userId;
|
||
final bool isFollowTab;
|
||
final VoidCallback? onFollowStatusChanged;
|
||
|
||
const FollowItem({
|
||
super.key,
|
||
required this.avatar,
|
||
required this.nickName,
|
||
required this.detail,
|
||
required this.followStatus,
|
||
required this.userId,
|
||
required this.isFollowTab,
|
||
this.onFollowStatusChanged,
|
||
});
|
||
|
||
@override
|
||
State<FollowItem> createState() => _FollowItemState();
|
||
}
|
||
|
||
class _FollowItemState extends State<FollowItem> {
|
||
late String _currentStatus;
|
||
// bool _isLoading = false;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_currentStatus = widget.followStatus;
|
||
}
|
||
|
||
String _getButtonText() {
|
||
if (widget.isFollowTab) {
|
||
return '已关注';
|
||
}
|
||
return _currentStatus == '1' ? '互相关注' : '回关';
|
||
}
|
||
|
||
Color _getButtonTextColor() {
|
||
if (widget.isFollowTab) {
|
||
return const Color(0xff6D717A);
|
||
}
|
||
return _currentStatus == '1'
|
||
? const Color(0xff6D717A)
|
||
: const Color(0xffFF4B4B); // 修改为红色
|
||
}
|
||
|
||
Future<void> _handleFollowAction() async {
|
||
// if (_isLoading) return;
|
||
|
||
// setState(() => _isLoading = true);
|
||
try {
|
||
if (widget.isFollowTab || _currentStatus == '1') {
|
||
// 显示取消关注确认框
|
||
final bool? shouldUnfollow = await showDialog<bool>(
|
||
context: context,
|
||
barrierDismissible: false,
|
||
builder: (BuildContext context) {
|
||
return CustomAlertDialog(
|
||
title: '确认不再关注?',
|
||
content: '',
|
||
onConfirm: () {
|
||
Navigator.of(context).pop(true);
|
||
},
|
||
confirmText: '确定',
|
||
cancelText: '取消',
|
||
);
|
||
},
|
||
);
|
||
|
||
if (shouldUnfollow == true) {
|
||
final result = await userApi.cancelFollow(widget.userId);
|
||
debugPrint('取消关注响应数据: $result');
|
||
if (result['code'] == 200) {
|
||
setState(() => _currentStatus = '0');
|
||
widget.onFollowStatusChanged?.call();
|
||
if (widget.isFollowTab) {
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
debugPrint('点击回关按钮');
|
||
// 直接关注
|
||
final result = await userApi.addFollow(widget.userId);
|
||
debugPrint('回关响应数据: $result');
|
||
if (result['code'] == 200) {
|
||
setState(() => _currentStatus = '1');
|
||
if (widget.isFollowTab) {
|
||
widget.onFollowStatusChanged?.call();
|
||
}
|
||
}
|
||
}
|
||
} catch (e) {
|
||
if (mounted) {
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
SnackBar(content: Text('操作失败: ${e.toString()}')),
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Row(
|
||
children: [
|
||
GestureDetector(
|
||
onTap: () {
|
||
Navigator.pushNamed(
|
||
context,
|
||
'/otherMine',
|
||
arguments: {
|
||
'userId': widget.userId,
|
||
'avatar': widget.avatar,
|
||
'nickName': widget.nickName,
|
||
'isFollow': widget.followStatus,
|
||
},
|
||
);
|
||
},
|
||
child: CircleAvatar(
|
||
backgroundImage: NetworkImage(widget.avatar),
|
||
radius: 24,
|
||
),
|
||
),
|
||
const SizedBox(width: 10),
|
||
Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
widget.nickName,
|
||
style: const TextStyle(fontSize: 16, color: Colors.white),
|
||
),
|
||
const SizedBox(height: 5),
|
||
SizedBox(
|
||
width: MediaQuery.of(context).size.width * 0.5,
|
||
child: Text(
|
||
widget.detail,
|
||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||
overflow: TextOverflow.ellipsis,
|
||
maxLines: 1,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
ElevatedButton(
|
||
style: ElevatedButton.styleFrom(
|
||
backgroundColor: Colors.transparent,
|
||
elevation: 0,
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius:const BorderRadius.all(Radius.circular(50)),
|
||
side: BorderSide(
|
||
// 根据状态动态设置边框颜色
|
||
color: widget.isFollowTab || _currentStatus == '1'
|
||
? const Color(0xff464952)
|
||
: const Color(0xffFF4B4B),
|
||
width: 1.5,
|
||
),
|
||
),
|
||
minimumSize: const Size(70, 26),
|
||
padding: const EdgeInsets.all(0),
|
||
),
|
||
onPressed: _handleFollowAction,
|
||
child: Text(
|
||
_getButtonText(),
|
||
style: TextStyle(
|
||
color: _getButtonTextColor(),
|
||
fontSize: 12,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
} |