flutter/lib/pages/groupChat/index.dart

392 lines
13 KiB
Dart
Raw Normal View History

2025-09-06 14:57:47 +08:00
// 创建群聊
2025-09-03 11:25:31 +08:00
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
2025-09-06 14:57:47 +08:00
import 'package:get/get.dart';
import 'package:loopin/IM/controller/im_user_info_controller.dart';
2025-09-13 17:01:01 +08:00
import 'package:loopin/IM/im_message.dart';
2025-09-06 14:57:47 +08:00
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/styles/index.dart';
import 'package:tencent_cloud_chat_sdk/enum/group_member_role_enum.dart';
import 'package:tencent_cloud_chat_sdk/enum/group_type.dart';
2025-09-13 17:01:01 +08:00
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
2025-09-06 14:57:47 +08:00
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
2025-09-03 11:25:31 +08:00
class StartGroupChatPage extends StatefulWidget {
const StartGroupChatPage({super.key});
@override
State<StartGroupChatPage> createState() => _StartGroupChatPageState();
}
class _StartGroupChatPageState extends State<StartGroupChatPage> {
final TextEditingController _searchController = TextEditingController();
2025-09-06 14:57:47 +08:00
List<V2TimUserFullInfo> dataList = [];
List<V2TimUserFullInfo> filteredList = [];
2025-09-03 11:25:31 +08:00
Set<String> selectedIds = {}; // 已选中的用户 id
2025-09-06 14:57:47 +08:00
String page = '';
2025-09-03 11:25:31 +08:00
bool hasMore = true;
2025-09-06 14:57:47 +08:00
bool isLoading = true;
2025-09-13 17:01:01 +08:00
bool makeGroup = true;
2025-09-03 11:25:31 +08:00
@override
void initState() {
super.initState();
_loadData(reset: true);
}
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
2025-09-13 17:01:01 +08:00
// 分页获取关注列表数据
2025-09-03 11:25:31 +08:00
Future<void> _loadData({bool reset = false}) async {
if (reset) {
2025-09-06 14:57:47 +08:00
page = '';
2025-09-03 11:25:31 +08:00
hasMore = true;
dataList.clear();
}
2025-09-06 14:57:47 +08:00
final res = await ImService.instance.getMutualFollowersList(
nextCursor: page,
2025-09-03 11:25:31 +08:00
);
2025-09-06 14:57:47 +08:00
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');
2025-09-03 11:25:31 +08:00
2025-09-06 14:57:47 +08:00
if (isFinished) {
setState(() {
hasMore = false;
});
// 加载没数据了
page = '';
} else {
page = res.data!.nextCursor ?? '';
}
logger.i('获取数据成功:$userInfoList');
setState(() {
dataList.addAll(userInfoList);
_applySearch(_searchController.text);
});
2025-09-03 11:25:31 +08:00
} else {
2025-09-06 14:57:47 +08:00
logger.e('获取数据失败:${res.desc}');
2025-09-03 11:25:31 +08:00
}
}
void _applySearch(String query) {
setState(() {
if (query.isEmpty) {
filteredList = List.from(dataList);
} else {
2025-09-06 14:57:47 +08:00
filteredList = dataList.where((item) => (item.nickName ?? '').contains(query.trim())).toList();
2025-09-03 11:25:31 +08:00
}
});
}
void _toggleSelection(String id) {
setState(() {
if (selectedIds.contains(id)) {
selectedIds.remove(id);
} else {
selectedIds.add(id);
}
});
}
2025-09-06 14:57:47 +08:00
/// 创建群聊
void createGroup(Set<String> selectedIds) async {
2025-09-13 17:01:01 +08:00
logger.w(makeGroup);
if (makeGroup == false) {
return;
}
setState(() {
makeGroup = false;
});
2025-09-06 14:57:47 +08:00
// dataList是原始数据List<V2TimUserFullInfo> dataList = [];
// 通过dataList和selectedIds来构建memberList
final ctl = Get.find<ImUserInfoController>();
final memberList = dataList
.where((user) => selectedIds.contains(user.userID))
.map((user) => V2TimGroupMember(
userID: user.userID!,
role: GroupMemberRoleTypeEnum.V2TIM_GROUP_MEMBER_ROLE_MEMBER, // 加群的成员角色(默认普通成员)
))
.toList();
final self = V2TimGroupMember(
userID: ctl.userID.value,
role: GroupMemberRoleTypeEnum.V2TIM_GROUP_MEMBER_ROLE_OWNER, // 建群的人为群主
);
memberList.insert(0, self);
final groupName = buildGroupName(selectedIds);
2025-09-13 17:01:01 +08:00
try {
final res = await ImService.instance.createGroup(groupType: GroupType.Work, groupName: groupName, memberList: memberList);
if (res.success) {
final groupID = res.data;
logger.w(groupID);
// final V2TimConversation conv = V2TimConversation(conversationID: 'group_$groupID');
// conv.showName = groupName;
final msgRes = await IMMessage().createTextMessage(text: '加入了群聊:$groupName');
if (msgRes.success && msgRes.data?.messageInfo != null) {
final groupRes = await IMMessage().sendMessage(
msg: msgRes.data!.messageInfo!,
groupID: groupID,
isExcludedFromUnreadCount: true,
groupName: groupName,
cloudCustomData: 'tips',
);
if (groupRes.success) {
final convRes = await ImService.instance.getConversation(conversationID: 'group_$groupID');
if (convRes.success) {
setState(() {
makeGroup = true;
});
final V2TimConversation conv = convRes.data;
logger.e(conv.toJson());
await Get.toNamed('/chatGroup', arguments: conv);
}
}
}
}
} catch (e) {
logger.e(e);
//修改按钮可操作状态
setState(() {
makeGroup = true;
});
2025-09-06 14:57:47 +08:00
}
}
// 构建默认群名称
String buildGroupName(Set<String> selectedIds) {
int maxLength = 20;
// 根据 selectedIds 找到对应的昵称
final memberNicknames = selectedIds.map((id) {
final user = dataList.firstWhere(
(u) => u.userID == id,
orElse: () => V2TimUserFullInfo(userID: id, nickName: id),
);
return user.nickName?.isNotEmpty == true ? user.nickName! : user.userID;
}).toList();
final ctl = Get.find<ImUserInfoController>();
// 插入群主的信息
memberNicknames.insert(0, ctl.nickname.value);
// 拼接成群名
String name = memberNicknames.join("");
// 如果超过长度限制,截断
if (name.length > maxLength) {
name = "${name.substring(0, maxLength - 3)}...";
}
return name;
}
// 空状态提示
Widget _emptyTip(String text) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset('assets/images/empty.png', width: 100),
const SizedBox(height: 8),
Text(
text,
style: const TextStyle(color: Colors.grey, fontSize: 13),
),
],
),
);
}
2025-09-03 11:25:31 +08:00
Widget _buildCard({
required IconData icon,
required String title,
required VoidCallback onTap,
}) {
return Card(
color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: ListTile(
leading: Icon(icon, color: Colors.black),
title: Text(title, style: const TextStyle(fontSize: 16)),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
onTap: onTap,
),
);
}
@override
Widget build(BuildContext context) {
return 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: const Text(
"创建群聊",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
body: Column(
children: [
// 上半部分 - 两个卡片
2025-09-13 17:01:01 +08:00
// Padding(
// padding: const EdgeInsets.all(8.0),
// child: Column(
// children: [
// _buildCard(
// icon: Icons.public,
// title: "创建公开群",
// onTap: () {
// logger.w("跳转到 创建公开群");
// },
// ),
// _buildCard(
// icon: Icons.group,
// title: "创建好友群",
// onTap: () {
// logger.w("跳转到 创建好友群");
// },
// ),
// ],
// ),
// ),
2025-09-03 11:25:31 +08:00
2025-09-13 17:01:01 +08:00
// const Divider(),
2025-09-03 11:25:31 +08:00
2025-09-06 14:57:47 +08:00
// 下半部分
2025-09-03 11:25:31 +08:00
Expanded(
child: Column(
children: [
// 搜索框
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _searchController,
decoration: InputDecoration(
hintText: "搜索用户昵称",
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
onChanged: _applySearch,
),
),
// 列表
Expanded(
child: EasyRefresh(
2025-09-06 14:57:47 +08:00
header: ClassicHeader(
dragText: '下拉刷新',
armedText: '释放刷新',
readyText: '加载中...',
processingText: '加载中...',
processedText: '加载完成',
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
footer: ClassicFooter(
dragText: '加载更多',
armedText: '释放加载',
readyText: '加载中...',
processingText: '加载中...',
2025-09-22 14:41:47 +08:00
processedText: '加载完成',
2025-09-06 14:57:47 +08:00
failedText: '加载失败,请重试',
2025-09-22 14:41:47 +08:00
noMoreText: '没有更多了~',
2025-09-06 14:57:47 +08:00
messageText: '最后更新于 %T',
),
2025-09-03 11:25:31 +08:00
onRefresh: () async => _loadData(reset: true),
onLoad: () async {
2025-09-22 14:41:47 +08:00
if (hasMore) {
await _loadData();
return hasMore ? IndicatorResult.success : IndicatorResult.noMore;
}
2025-09-03 11:25:31 +08:00
},
2025-09-06 14:57:47 +08:00
child: filteredList.isEmpty
? _emptyTip('暂无数据')
: ListView.builder(
itemCount: filteredList.length,
itemBuilder: (context, index) {
final item = filteredList[index];
final id = item.userID as String;
final isSelected = selectedIds.contains(id);
return ListTile(
leading: ClipOval(
// child: Text((item.nickName ?? '未知').substring(0, 1)),
child: NetworkOrAssetImage(
imageUrl: item.faceUrl,
width: 48,
height: 48,
),
),
title: Text(item.nickName ?? '未知'),
trailing: Checkbox(
value: isSelected,
shape: const CircleBorder(),
onChanged: (_) => _toggleSelection(id),
),
onTap: () => _toggleSelection(id),
);
},
2025-09-03 11:25:31 +08:00
),
),
),
],
),
),
],
),
// 底部按钮
bottomNavigationBar: SafeArea(
2025-09-06 14:57:47 +08:00
child: Padding(
padding: const EdgeInsets.all(12.0),
child: TweenAnimationBuilder<Color?>(
duration: const Duration(milliseconds: 300),
tween: ColorTween(
begin: Colors.grey,
2025-09-13 17:01:01 +08:00
end: (selectedIds.isNotEmpty && makeGroup == true) ? FStyle.primaryColor : Colors.grey,
2025-09-03 11:25:31 +08:00
),
2025-09-06 14:57:47 +08:00
builder: (context, bgColor, _) {
return ElevatedButton(
2025-09-13 17:01:01 +08:00
onPressed: (selectedIds.isEmpty && makeGroup == false)
2025-09-06 14:57:47 +08:00
? null
: () {
logger.w("选择了用户:$selectedIds");
createGroup(selectedIds);
},
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(50),
backgroundColor: bgColor,
shape: const StadiumBorder(), // 胶囊形状
),
child: AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 300),
style: TextStyle(
fontSize: 16,
color: selectedIds.isEmpty ? Colors.black : Colors.white,
),
child: const Text("发起聊天"),
),
);
},
2025-09-03 11:25:31 +08:00
),
2025-09-06 14:57:47 +08:00
)),
2025-09-03 11:25:31 +08:00
);
}
}