// 创建群聊 import 'package:easy_refresh/easy_refresh.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:loopin/IM/controller/im_user_info_controller.dart'; 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'; 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'; import 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_conversation.dart'; class StartGroupChatPage extends StatefulWidget { const StartGroupChatPage({super.key}); @override State createState() => _StartGroupChatPageState(); } class _StartGroupChatPageState extends State { final TextEditingController _searchController = TextEditingController(); List dataList = []; List filteredList = []; Set selectedIds = {}; // 已选中的用户 id String page = ''; bool hasMore = true; bool isLoading = true; @override void initState() { super.initState(); _loadData(reset: true); } @override void dispose() { _searchController.dispose(); super.dispose(); } // 分页获取陌关注列表数据 Future _loadData({bool reset = false}) async { if (reset) { page = ''; hasMore = true; dataList.clear(); } final res = await ImService.instance.getMutualFollowersList( nextCursor: page, ); 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) { setState(() { hasMore = false; }); // 加载没数据了 page = ''; } else { page = res.data!.nextCursor ?? ''; } logger.i('获取数据成功:$userInfoList'); setState(() { dataList.addAll(userInfoList); _applySearch(_searchController.text); }); } else { logger.e('获取数据失败:${res.desc}'); } } void _applySearch(String query) { setState(() { if (query.isEmpty) { filteredList = List.from(dataList); } else { filteredList = dataList.where((item) => (item.nickName ?? '').contains(query.trim())).toList(); } }); } void _toggleSelection(String id) { setState(() { if (selectedIds.contains(id)) { selectedIds.remove(id); } else { selectedIds.add(id); } }); } /// 创建群聊 void createGroup(Set selectedIds) async { // dataList是原始数据List dataList = []; // 通过dataList和selectedIds来构建memberList final ctl = Get.find(); 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); 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; Get.toNamed('/chatGroup', arguments: conv); } } // 构建默认群名称 String buildGroupName(Set 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(); // 插入群主的信息 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), ), ], ), ); } 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: [ // 上半部分 - 两个卡片 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("跳转到 创建好友群"); // }, // ), ], ), ), const Divider(), // 下半部分 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( header: ClassicHeader( dragText: '下拉刷新', armedText: '释放刷新', readyText: '加载中...', processingText: '加载中...', processedText: '加载完成', failedText: '加载失败,请重试', messageText: '最后更新于 %T', ), footer: ClassicFooter( dragText: '加载更多', armedText: '释放加载', readyText: '加载中...', processingText: '加载中...', processedText: hasMore ? '加载完成' : '没有更多了~', failedText: '加载失败,请重试', messageText: '最后更新于 %T', ), onRefresh: () async => _loadData(reset: true), onLoad: () async { if (hasMore) await _loadData(); }, 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), ); }, ), ), ), ], ), ), ], ), // 底部按钮 bottomNavigationBar: SafeArea( child: Padding( padding: const EdgeInsets.all(12.0), child: TweenAnimationBuilder( duration: const Duration(milliseconds: 300), tween: ColorTween( begin: Colors.grey, end: selectedIds.isEmpty ? Colors.grey : FStyle.primaryColor, ), builder: (context, bgColor, _) { return ElevatedButton( onPressed: selectedIds.isEmpty ? 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("发起聊天"), ), ); }, ), )), ); } }