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

305 lines
11 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:get/get.dart';
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/pages/groupChat/controller/group_detail_controller.dart';
import 'package:loopin/styles/index.dart';
import 'package:tencent_cloud_chat_sdk/enum/group_member_filter_enum.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_search_param.dart';
class MemberActionSheet extends StatefulWidget {
final String groupID;
final Function(List<String> selected) onAction;
final String title;
final String actionLabel;
final bool showButton;
final bool showSelf;
const MemberActionSheet({
super.key,
required this.groupID,
required this.onAction,
this.title = "",
this.actionLabel = "确认",
this.showButton = true,
this.showSelf = false,
});
@override
State<MemberActionSheet> createState() => _MemberActionSheetState();
}
class _MemberActionSheetState extends State<MemberActionSheet> {
String _query = "";
final Set<String> _selectedIDs = {}; // 选中的 userID 集合
late List<V2TimGroupMemberFullInfo> members = [];
late List<V2TimGroupMemberFullInfo> searchMemberList = [];
String nextSeq = '0';
String searchCursor = '';
bool hasMore = false; // 普通列表用
bool loading = false;
bool isFinished = false; // 搜索列表用
bool loading2 = false; //搜索
//
@override
void initState() {
super.initState();
if (widget.showSelf) {
final self = Get.find<GroupDetailController>().selfInfo.value!;
members.insert(0, self);
}
getMemberData();
}
//群成员
Future<void> getMemberData() async {
if (loading) return;
loading = true;
final res = await ImService.instance.getGroupMemberList(
groupID: widget.groupID,
filter: GroupMemberFilterTypeEnum.V2TIM_GROUP_MEMBER_FILTER_COMMON,
nextSeq: nextSeq,
count: 100,
);
if (res.success && res.data != null) {
logger.e(res.data!.nextSeq);
nextSeq = res.data!.nextSeq ?? '0';
final mem = res.data!.memberInfoList ?? [];
setState(() {
members.addAll(mem);
2025-09-22 14:41:47 +08:00
hasMore = res.data!.nextSeq == '0' ? false : true;
2025-09-13 17:01:01 +08:00
loading = false;
});
}
}
///搜索群成员
Future<void> searchMember({bool loadMore = false}) async {
if (loading2) return;
if (_query.isEmpty) {
setState(() {
_query = '';
});
return;
}
loading2 = true;
final param = V2TimGroupMemberSearchParam(
keywordList: [_query],
groupIDList: [widget.groupID],
isSearchMemberUserID: true,
isSearchMemberNickName: true,
isSearchMemberRemark: true,
isSearchMemberNameCard: true,
keywordListMatchType: V2TimGroupMemberSearchParam.V2TIM_KEYWORD_LIST_MATCH_TYPE_OR,
searchCount: 100,
searchCursor: searchCursor,
);
final res = await ImService.instance.searchGroupMembers(param: param);
if (res.success && res.data != null) {
isFinished = res.data!.isFinished ?? true;
final searchCursor = res.data!.nextCursor ?? '';
final data = res.data!.groupMemberSearchResultItems;
// 搜索结果群的成员
if (data != null && data[widget.groupID] != null) {
List<V2TimGroupMemberFullInfo> list = (data[widget.groupID] as List<dynamic>).cast<V2TimGroupMemberFullInfo>();
if (!widget.showSelf) {
// 过滤自己
final self = Get.find<GroupDetailController>().selfInfo.value!;
list = list.where((item) => item.userID != self.userID).toList();
}
if (loadMore) {
searchMemberList.addAll(list);
} else {
searchMemberList = list;
}
}
logger.w(hasMore);
logger.w(searchCursor);
logger.w(data);
}
setState(() {
loading2 = false;
});
}
String handleText(String? text, String defaultValue) {
if (text == null || text.trim().isEmpty) return defaultValue;
return text;
}
@override
Widget build(BuildContext context) {
List filteredMembers;
if (_query.isEmpty) {
filteredMembers = members;
} else {
filteredMembers = searchMemberList;
}
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();
searchMember();
});
},
),
),
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 {
//
2025-09-22 14:41:47 +08:00
if (_query.isNotEmpty && (!isFinished)) {
await searchMember(loadMore: true);
return !isFinished ? IndicatorResult.success : IndicatorResult.noMore;
} else if (hasMore) {
await getMemberData();
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.nameCard, '');
final nickname = handleText(m.nickName, '未知昵称');
final showName = uname.isEmpty ? nickname : uname;
return InkWell(
onTap: () {
setState(() {
if (_selectedIDs.contains(id)) {
_selectedIDs.remove(id);
} else {
_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,
),
),
if (widget.showButton)
// 复选框
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,
),
),
),
),
],
),
),
),
);
}
}