flutter/lib/pages/groupChat/components/member_action_sheet.dart
2025-09-13 17:01:01 +08:00

303 lines
11 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
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: '加载中...',
processedText: hasMore ? '加载完成' : '没有更多了~',
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
onLoad: () async {
//
if (hasMore) {
if (_query.isNotEmpty) {
await searchMember(loadMore: true);
} else {
await getMemberData();
}
}
},
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,
),
),
),
),
],
),
),
),
);
}
}