flutter/lib/pages/groupChat/components/invite_action_sheet.dart

266 lines
9.6 KiB
Dart
Raw Normal View History

2025-09-13 17:01:01 +08:00
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/styles/index.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
class InviteActionSheet extends StatefulWidget {
final String groupID;
final Function(List<String> selected) onAction;
final String title;
final String actionLabel;
final bool showButton;
const InviteActionSheet({
super.key,
required this.groupID,
required this.onAction,
this.title = "",
this.actionLabel = "确认",
this.showButton = true,
});
@override
State<InviteActionSheet> createState() => _MemberActionSheetState();
}
class _MemberActionSheetState extends State<InviteActionSheet> {
String _query = "";
final Set<String> _selectedIDs = {}; // 选中的 userID 集合
late List<V2TimUserFullInfo> members = [];
String nextSeq = '';
bool hasMore = false;
bool loading = false;
//
@override
void initState() {
super.initState();
getMemberData();
}
//互关好友
Future<void> getMemberData({bool reset = false}) async {
if (loading) return;
loading = true;
final res = await ImService.instance.getMutualFollowersList(
nextCursor: nextSeq,
);
if (res.success && res.data != null) {
final userInfoList = res.data!.userFullInfoList ?? [];
final isFinished = res.data!.nextCursor == null || res.data!.nextCursor!.isEmpty;
logger.w('获取成功:${res.data!.nextCursor},是否还有更多:$isFinished');
if (isFinished) {
hasMore = false;
nextSeq = '';
} else {
nextSeq = res.data!.nextCursor ?? '';
hasMore = nextSeq.isNotEmpty ? true : false;
}
final ids = userInfoList.map((item) => item.userID).whereType<String>().toList();
if (ids.isNotEmpty) {
getGroupMemberData(userIDs: ids, userList: userInfoList);
}
} else {
logger.e('获取数据失败:${res.desc}');
}
}
//群成员
Future<void> getGroupMemberData({required List<String> userIDs, required List<V2TimUserFullInfo> userList}) async {
final res = await ImService.instance.getGroupMembersInfo(
groupID: widget.groupID,
memberList: userIDs,
);
if (res.success && res.data != null) {
final List<V2TimGroupMemberFullInfo> groupMemberList = res.data ?? [];
// 已经在群里的 userID
final inGroupIds = groupMemberList.map((m) => m.userID).whereType<String>().where((id) => id.isNotEmpty).toSet();
// 过滤掉已经在群里的
final notInGroupUsers = userList.where((user) => user.userID != null && !inGroupIds.contains(user.userID)).toList();
setState(() {
members.addAll(notInGroupUsers);
});
}
setState(() {
loading = false;
});
}
String handleText(String? text, String defaultValue) {
if (text == null || text.trim().isEmpty) return defaultValue;
return text;
}
@override
Widget build(BuildContext context) {
// 搜索过滤
final filteredMembers = members.where((m) {
final name = m.nickName ?? '';
return name.contains(_query);
}).toList();
return SafeArea(
child: GestureDetector(
behavior: HitTestBehavior.translucent, // 点击空白区域也能触发
onTap: () {
FocusScope.of(context).unfocus();
},
child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
centerTitle: true,
forceMaterialTransparency: true,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0),
child: Container(
color: Colors.grey[300],
height: 1.0,
),
),
title: Text(
widget.title,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.pop(context);
},
),
),
body: Column(
children: [
SizedBox(height: 10),
// 搜索框
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: TextField(
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search),
hintText: "搜索",
contentPadding: const EdgeInsets.symmetric(vertical: 8),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
),
onChanged: (value) {
setState(() {
_query = value.trim();
});
},
),
),
const SizedBox(height: 8),
// 成员列表
Expanded(
child: EasyRefresh(
footer: ClassicFooter(
dragText: '加载更多',
armedText: '释放加载',
readyText: '加载中...',
processingText: '加载中...',
2025-09-22 14:41:47 +08:00
processedText: '加载完成',
noMoreText: '没有更多了~',
2025-09-13 17:01:01 +08:00
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
onLoad: () async {
if (hasMore) {
await getMemberData();
2025-09-22 14:41:47 +08:00
return hasMore ? IndicatorResult.success : IndicatorResult.noMore;
2025-09-13 17:01:01 +08:00
}
},
child: ListView.builder(
itemCount: filteredMembers.length,
itemBuilder: (context, index) {
final m = filteredMembers[index];
final id = m.userID;
final uname = handleText(m.nickName, '');
final nickname = handleText(m.nickName, '未知昵称');
final showName = uname.isEmpty ? nickname : uname;
return InkWell(
onTap: () {
setState(() {
if (_selectedIDs.contains(id)) {
_selectedIDs.remove(id);
} else if (id != null && id.isNotEmpty) {
_selectedIDs.add(id);
}
});
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: Row(
children: [
// 左侧圆形头像
CircleAvatar(
radius: 20,
backgroundImage: m.faceUrl != null ? NetworkImage(m.faceUrl!) : null,
child: m.faceUrl == null ? const Icon(Icons.person) : null,
),
const SizedBox(width: 12),
// 用户名
Expanded(
child: Text(
showName,
style: const TextStyle(fontSize: 14),
overflow: TextOverflow.ellipsis,
),
),
// 复选框
Container(
width: 24,
height: 24,
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: _selectedIDs.contains(id) ? FStyle.primaryColor : Colors.grey),
color: _selectedIDs.contains(id) ? FStyle.primaryColor : Colors.transparent,
),
child: _selectedIDs.contains(id) ? const Icon(Icons.check, size: 16, color: Colors.white) : null,
),
],
),
),
);
},
),
),
),
// 底部操作按钮
if (widget.showButton)
Padding(
padding: const EdgeInsets.all(16),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 48),
backgroundColor: FStyle.primaryColor,
),
onPressed: _selectedIDs.isEmpty
? null
: () {
Navigator.pop(context);
widget.onAction(_selectedIDs.toList());
},
child: Text(
"${widget.actionLabel}${_selectedIDs.length}",
style: TextStyle(
color: _selectedIDs.isNotEmpty ? Colors.white : Colors.black,
),
),
),
),
],
),
),
),
);
}
}