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

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