feat: 群组信息自定义

This commit is contained in:
Zeew 2025-07-15 22:14:08 +08:00
parent a7149a29c3
commit e5d1225f7a
3 changed files with 244 additions and 128 deletions

View File

@ -18,29 +18,57 @@ import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/theme/color.dart'; import 'package:tencent_cloud_chat_uikit/theme/color.dart';
import 'package:tencent_cloud_chat_uikit/theme/tui_theme_view_model.dart'; import 'package:tencent_cloud_chat_uikit/theme/tui_theme_view_model.dart';
typedef GroupItemBuilder = Widget Function(BuildContext context, V2TimGroupInfo groupInfo); typedef GroupItemBuilder =
Widget Function(BuildContext context, V2TimGroupInfo groupInfo);
/// TIMUIKitGroup State的类型定义
typedef TIMUIKitGroupState = _TIMUIKitGroupState;
class TIMUIKitGroup extends StatefulWidget { class TIMUIKitGroup extends StatefulWidget {
final void Function(V2TimGroupInfo groupInfo, V2TimConversation conversation)? onTapItem; final void Function(V2TimGroupInfo groupInfo, V2TimConversation conversation)?
onTapItem;
final Widget Function(BuildContext context)? emptyBuilder; final Widget Function(BuildContext context)? emptyBuilder;
final GroupItemBuilder? itemBuilder; final GroupItemBuilder? itemBuilder;
/// the filter for group conversation /// the filter for group conversation
final bool Function(V2TimGroupInfo? groupInfo)? groupCollector; final bool Function(V2TimGroupInfo? groupInfo)? groupCollector;
const TIMUIKitGroup({Key? key, this.onTapItem, this.emptyBuilder, this.itemBuilder, this.groupCollector}) const TIMUIKitGroup({
: super(key: key); Key? key,
this.onTapItem,
this.emptyBuilder,
this.itemBuilder,
this.groupCollector,
}) : super(key: key);
@override @override
State<StatefulWidget> createState() => _TIMUIKitGroupState(); State<StatefulWidget> createState() => _TIMUIKitGroupState();
///
/// Static method to refresh group list data
static void refreshGroupListData(GlobalKey<TIMUIKitGroupState> key) {
key.currentState?.refreshGroupListData();
}
} }
class _TIMUIKitGroupState extends TIMUIKitState<TIMUIKitGroup> { class _TIMUIKitGroupState extends TIMUIKitState<TIMUIKitGroup> {
final TUIFriendShipViewModel _friendshipViewModel = serviceLocator<TUIFriendShipViewModel>(); final TUIFriendShipViewModel _friendshipViewModel =
final TUIGroupListenerModel _groupListenerModel = serviceLocator<TUIGroupListenerModel>(); serviceLocator<TUIFriendShipViewModel>();
final TUIGroupListenerModel _groupListenerModel =
serviceLocator<TUIGroupListenerModel>();
List<ISuspensionBeanImpl<V2TimGroupInfo>> _getShowList(List<V2TimGroupInfo> groupList) { ///
final List<ISuspensionBeanImpl<V2TimGroupInfo>> showList = List.empty(growable: true); /// Refresh group list data
void refreshGroupListData() {
_friendshipViewModel.loadGroupListData();
}
List<ISuspensionBeanImpl<V2TimGroupInfo>> _getShowList(
List<V2TimGroupInfo> groupList,
) {
final List<ISuspensionBeanImpl<V2TimGroupInfo>> showList = List.empty(
growable: true,
);
for (var i = 0; i < groupList.length; i++) { for (var i = 0; i < groupList.length; i++) {
final item = groupList[i]; final item = groupList[i];
@ -63,10 +91,16 @@ class _TIMUIKitGroupState extends TIMUIKitState<TIMUIKitGroup> {
final theme = Provider.of<TUIThemeViewModel>(context).theme; final theme = Provider.of<TUIThemeViewModel>(context).theme;
final showName = groupInfo.groupName ?? groupInfo.groupID; final showName = groupInfo.groupName ?? groupInfo.groupID;
final faceUrl = groupInfo.faceUrl ?? ""; final faceUrl = groupInfo.faceUrl ?? "";
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))), border: Border(
bottom: BorderSide(
color: theme.weakDividerColor ?? CommonColor.weakDividerColor,
),
),
),
child: Material( child: Material(
color: isDesktopScreen ? theme.wideBackgroundColor : null, color: isDesktopScreen ? theme.wideBackgroundColor : null,
child: InkWell( child: InkWell(
@ -82,7 +116,9 @@ class _TIMUIKitGroupState extends TIMUIKitState<TIMUIKitGroup> {
); );
final res = await TencentImSDKPlugin.v2TIMManager final res = await TencentImSDKPlugin.v2TIMManager
.getConversationManager() .getConversationManager()
.getConversation(conversationID: "group_${groupInfo.groupID}"); .getConversation(
conversationID: "group_${groupInfo.groupID}",
);
if (res.code == 0 && res.data != null) { if (res.code == 0 && res.data != null) {
conversation = res.data!; conversation = res.data!;
} }
@ -107,14 +143,18 @@ class _TIMUIKitGroupState extends TIMUIKitState<TIMUIKitGroup> {
), ),
), ),
Expanded( Expanded(
child: Container( child: Container(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(top: 10, bottom: 20), padding: const EdgeInsets.only(top: 10, bottom: 20),
child: Text( child: Text(
showName, showName,
style: TextStyle(color: Colors.black, fontSize: isDesktopScreen ? 14 : 18), style: TextStyle(
color: Colors.black,
fontSize: isDesktopScreen ? 14 : 18,
),
),
), ),
)) ),
], ],
), ),
), ),
@ -139,10 +179,14 @@ class _TIMUIKitGroupState extends TIMUIKitState<TIMUIKitGroup> {
providers: [ providers: [
ChangeNotifierProvider.value(value: _friendshipViewModel), ChangeNotifierProvider.value(value: _friendshipViewModel),
ChangeNotifierProvider.value(value: _groupListenerModel), ChangeNotifierProvider.value(value: _groupListenerModel),
ChangeNotifierProvider.value(value: serviceLocator<TUIThemeViewModel>()), ChangeNotifierProvider.value(
value: serviceLocator<TUIThemeViewModel>(),
),
], ],
builder: (BuildContext context, Widget? w) { builder: (BuildContext context, Widget? w) {
final NeedUpdate? needUpdate = Provider.of<TUIGroupListenerModel>(context).needUpdate; final NeedUpdate? needUpdate = Provider.of<TUIGroupListenerModel>(
context,
).needUpdate;
if (needUpdate != null) { if (needUpdate != null) {
_groupListenerModel.needUpdate = null; _groupListenerModel.needUpdate = null;
switch (needUpdate.updateType) { switch (needUpdate.updateType) {
@ -156,20 +200,23 @@ class _TIMUIKitGroupState extends TIMUIKitState<TIMUIKitGroup> {
break; break;
} }
} }
List<V2TimGroupInfo> groupList = Provider.of<TUIFriendShipViewModel>(context).groupList; List<V2TimGroupInfo> groupList = Provider.of<TUIFriendShipViewModel>(
context,
).groupList;
if (widget.groupCollector != null) { if (widget.groupCollector != null) {
groupList = groupList.where(widget.groupCollector!).toList(); groupList = groupList.where(widget.groupCollector!).toList();
} }
if (groupList.isNotEmpty) { if (groupList.isNotEmpty) {
final showList = _getShowList(groupList); final showList = _getShowList(groupList);
return AZListViewContainer( return AZListViewContainer(
isShowIndexBar: false, isShowIndexBar: false,
memberList: showList, memberList: showList,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final groupInfo = showList[index].memberInfo; final groupInfo = showList[index].memberInfo;
final itemBuilder = _getItemBuilder(); final itemBuilder = _getItemBuilder();
return itemBuilder(context, groupInfo); return itemBuilder(context, groupInfo);
}); },
);
} }
if (widget.emptyBuilder != null) { if (widget.emptyBuilder != null) {

View File

@ -20,8 +20,13 @@ import 'package:tencent_cloud_chat_uikit/theme/color.dart';
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart'; import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
class GroupMemberTitle extends TIMUIKitStatelessWidget { class GroupMemberTitle extends TIMUIKitStatelessWidget {
final VoidCallback? onAddMemberPressed;
final VoidCallback? onRemoveMemberPressed;
GroupMemberTitle({ GroupMemberTitle({
Key? key, Key? key,
this.onAddMemberPressed,
this.onRemoveMemberPressed,
}) : super(key: key); }) : super(key: key);
List<V2TimGroupMemberFullInfo?> _getMemberList(memberList, int showRange) { List<V2TimGroupMemberFullInfo?> _getMemberList(memberList, int showRange) {
@ -40,8 +45,14 @@ class GroupMemberTitle extends TIMUIKitStatelessWidget {
return friendRemark != "" ? friendRemark : showName; return friendRemark != "" ? friendRemark : showName;
} }
List<Widget> _groupMemberListBuilder(List memberList, TUITheme theme, TUIGroupProfileModel model, int showRange) { List<Widget> _groupMemberListBuilder(
final isDesktopScreen = TUIKitScreenUtils.getFormFactor() == DeviceType.Desktop; List memberList,
TUITheme theme,
TUIGroupProfileModel model,
int showRange,
) {
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor() == DeviceType.Desktop;
return _getMemberList(memberList, showRange).map((element) { return _getMemberList(memberList, showRange).map((element) {
final faceUrl = element?.faceUrl ?? ""; final faceUrl = element?.faceUrl ?? "";
final showName = _getShowName(element); final showName = _getShowName(element);
@ -61,22 +72,25 @@ class GroupMemberTitle extends TIMUIKitStatelessWidget {
width: isDesktopScreen ? 36 : 50, width: isDesktopScreen ? 36 : 50,
height: isDesktopScreen ? 36 : 50, height: isDesktopScreen ? 36 : 50,
child: Avatar( child: Avatar(
borderRadius: isDesktopScreen ? BorderRadius.circular(18) : null, borderRadius: isDesktopScreen
? BorderRadius.circular(18)
: null,
faceUrl: faceUrl, faceUrl: faceUrl,
showName: showName, showName: showName,
type: 1, type: 1,
), ),
), ),
if (!isDesktopScreen) if (!isDesktopScreen) const SizedBox(height: 8),
const SizedBox(
height: 8,
),
if (!isDesktopScreen) if (!isDesktopScreen)
Text( Text(
showName, showName,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(overflow: TextOverflow.ellipsis, color: theme.weakTextColor, fontSize: 10), style: TextStyle(
) overflow: TextOverflow.ellipsis,
color: theme.weakTextColor,
fontSize: 10,
),
),
], ],
), ),
), ),
@ -84,35 +98,51 @@ class GroupMemberTitle extends TIMUIKitStatelessWidget {
}).toList(); }).toList();
} }
List<Widget> _inviteMemberBuilder(bool isCanInviteMember, bool isCanKickOffMember, theme, BuildContext context) { List<Widget> _inviteMemberBuilder(
bool isCanInviteMember,
bool isCanKickOffMember,
theme,
BuildContext context,
) {
return []; return [];
} }
void navigateToMemberList( void navigateToMemberList(
BuildContext context, TUIGroupProfileModel model, List<V2TimGroupMemberFullInfo?> memberList) { BuildContext context,
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; TUIGroupProfileModel model,
List<V2TimGroupMemberFullInfo?> memberList,
) {
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
if (!isDesktopScreen) { if (!isDesktopScreen) {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => GroupProfileMemberListPage(model: model, memberList: memberList), builder: (context) =>
)); GroupProfileMemberListPage(model: model, memberList: memberList),
),
);
} else { } else {
final option1 = memberList.length.toString(); final option1 = memberList.length.toString();
TUIKitWidePopup.showPopupWindow( TUIKitWidePopup.showPopupWindow(
operationKey: TUIKitWideModalOperationKey.groupMembersList, operationKey: TUIKitWideModalOperationKey.groupMembersList,
context: context, context: context,
width: MediaQuery.of(context).size.width * 0.5, width: MediaQuery.of(context).size.width * 0.5,
height: MediaQuery.of(context).size.height * 0.8, height: MediaQuery.of(context).size.height * 0.8,
title: TIM_t_para("群成员({{option1}}人)", "群成员($option1人)")(option1: option1), title: TIM_t_para("群成员({{option1}}人)", "群成员($option1人)")(
child: (onClose) => GroupProfileMemberListPage(model: model, memberList: memberList)); option1: option1,
),
child: (onClose) =>
GroupProfileMemberListPage(model: model, memberList: memberList),
);
} }
} }
@override @override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUITheme theme = value.theme; final TUITheme theme = value.theme;
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final model = Provider.of<TUIGroupProfileModel>(context); final model = Provider.of<TUIGroupProfileModel>(context);
final memberAmount = model.groupInfo?.memberCount ?? 0; final memberAmount = model.groupInfo?.memberCount ?? 0;
final option1 = memberAmount.toString(); final option1 = memberAmount.toString();
@ -139,7 +169,14 @@ class GroupMemberTitle extends TIMUIKitStatelessWidget {
decoration: isDesktopScreen decoration: isDesktopScreen
? null ? null
: BoxDecoration( : BoxDecoration(
border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))), border: Border(
bottom: BorderSide(
color:
theme.weakDividerColor ??
CommonColor.weakDividerColor,
),
),
),
child: InkWell( child: InkWell(
onTap: () async { onTap: () async {
navigateToMemberList(context, model, memberList); navigateToMemberList(context, model, memberList);
@ -147,19 +184,30 @@ class GroupMemberTitle extends TIMUIKitStatelessWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(TIM_t("群成员"), style: TextStyle(color: theme.darkTextColor, fontSize: isDesktopScreen ? 14 : 16)), Text(
TIM_t("群成员"),
style: TextStyle(
color: theme.darkTextColor,
fontSize: isDesktopScreen ? 14 : 16,
),
),
Row( Row(
children: [ children: [
Text( Text(
TIM_t_para("{{option1}}人", "$option1人")(option1: option1), TIM_t_para("{{option1}}人", "$option1人")(
style: TextStyle(color: theme.darkTextColor, fontSize: isDesktopScreen ? 14 : 16), option1: option1,
),
style: TextStyle(
color: theme.darkTextColor,
fontSize: isDesktopScreen ? 14 : 16,
),
), ),
Icon( Icon(
Icons.keyboard_arrow_right, Icons.keyboard_arrow_right,
color: theme.weakTextColor, color: theme.weakTextColor,
), ),
], ],
) ),
], ],
), ),
), ),
@ -171,25 +219,30 @@ class GroupMemberTitle extends TIMUIKitStatelessWidget {
}, },
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(width: 1, color: theme.weakDividerColor ?? CommonColor.weakDividerColor), border: Border.all(
width: 1,
color:
theme.weakDividerColor ?? CommonColor.weakDividerColor,
),
borderRadius: const BorderRadius.all(Radius.circular(4)), borderRadius: const BorderRadius.all(Radius.circular(4)),
), ),
// height: 30, // height: 30,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 6,
),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Icon( Icon(Icons.search, color: hexToColor("979797"), size: 16),
Icons.search,
color: hexToColor("979797"),
size: 16,
),
const SizedBox(width: 6), const SizedBox(width: 6),
Text(TIM_t("搜索"), Text(
style: TextStyle( TIM_t("搜索"),
color: theme.weakTextColor, style: TextStyle(
fontSize: 12, color: theme.weakTextColor,
)), fontSize: 12,
),
),
], ],
), ),
), ),
@ -205,67 +258,76 @@ class GroupMemberTitle extends TIMUIKitStatelessWidget {
..._groupMemberListBuilder(memberList, theme, model, showRange), ..._groupMemberListBuilder(memberList, theme, model, showRange),
if (isCanInviteMember) if (isCanInviteMember)
DottedBorder( DottedBorder(
borderType: BorderType.RRect, borderType: BorderType.RRect,
radius: Radius.circular(isDesktopScreen ? 18 : 4.5), radius: Radius.circular(isDesktopScreen ? 18 : 4.5),
color: theme.weakTextColor!, color: theme.weakTextColor!,
dashPattern: const [6, 3], dashPattern: const [6, 3],
child: SizedBox( child: SizedBox(
width: isDesktopScreen ? 32 : 48, width: isDesktopScreen ? 32 : 48,
height: isDesktopScreen ? 32 : 48, height: isDesktopScreen ? 32 : 48,
child: IconButton( child: IconButton(
onPressed: () { onPressed: () {
if (onAddMemberPressed != null) {
onAddMemberPressed!();
} else {
if (isDesktopScreen) { if (isDesktopScreen) {
TUIKitWidePopup.showPopupWindow( TUIKitWidePopup.showPopupWindow(
context: context, context: context,
operationKey: TUIKitWideModalOperationKey.addGroupMembers, operationKey:
width: 350, TUIKitWideModalOperationKey.addGroupMembers,
title: TIM_t("添加群成员"), width: 350,
height: 460, title: TIM_t("添加群成员"),
onSubmit: () { height: 460,
addGroupMemberKey.currentState?.submitAdd(); onSubmit: () {
}, addGroupMemberKey.currentState?.submitAdd();
child: (onClose) => AddGroupMemberPage( },
model: model, child: (onClose) => AddGroupMemberPage(
onClose: onClose, model: model,
key: addGroupMemberKey, onClose: onClose,
)); key: addGroupMemberKey,
),
);
} else { } else {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => AddGroupMemberPage( builder: (context) =>
model: model, AddGroupMemberPage(model: model),
), ),
)); );
} }
}, }
icon: Icon( },
Icons.add, icon: Icon(Icons.add, size: isDesktopScreen ? 16 : 18),
size: isDesktopScreen ? 16 : 18, color: theme.weakTextColor,
), ),
color: theme.weakTextColor, ),
), ),
)),
if (isCanKickOffMember) if (isCanKickOffMember)
DottedBorder( DottedBorder(
borderType: BorderType.RRect, borderType: BorderType.RRect,
radius: Radius.circular(isDesktopScreen ? 18 : 4.5), radius: Radius.circular(isDesktopScreen ? 18 : 4.5),
color: theme.weakTextColor!, color: theme.weakTextColor!,
dashPattern: const [6, 3], dashPattern: const [6, 3],
child: SizedBox( child: SizedBox(
width: isDesktopScreen ? 32 : 48, width: isDesktopScreen ? 32 : 48,
height: isDesktopScreen ? 32 : 48, height: isDesktopScreen ? 32 : 48,
child: IconButton( child: IconButton(
onPressed: () { onPressed: () {
if (onRemoveMemberPressed != null) {
onRemoveMemberPressed!();
} else {
if (isDesktopScreen) { if (isDesktopScreen) {
TUIKitWidePopup.showPopupWindow( TUIKitWidePopup.showPopupWindow(
operationKey: TUIKitWideModalOperationKey.kickOffGroupMembers, operationKey: TUIKitWideModalOperationKey
.kickOffGroupMembers,
context: context, context: context,
width: 350, width: 350,
title: TIM_t("删除群成员"), title: TIM_t("删除群成员"),
height: 460, height: 460,
onSubmit: () { onSubmit: () {
deleteGroupMemberKey.currentState?.submitDelete(); deleteGroupMemberKey.currentState
?.submitDelete();
}, },
child: (onClose) => DeleteGroupMemberPage( child: (onClose) => DeleteGroupMemberPage(
model: model, model: model,
@ -275,19 +337,23 @@ class GroupMemberTitle extends TIMUIKitStatelessWidget {
); );
} else { } else {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => DeleteGroupMemberPage(model: model), builder: (context) =>
)); DeleteGroupMemberPage(model: model),
),
);
} }
}, }
icon: Icon( },
Icons.remove, icon: Icon(
size: isDesktopScreen ? 16 : 18, Icons.remove,
), size: isDesktopScreen ? 16 : 18,
color: theme.weakTextColor,
), ),
)), color: theme.weakTextColor,
),
),
),
], ],
), ),
), ),
@ -298,7 +364,10 @@ class GroupMemberTitle extends TIMUIKitStatelessWidget {
margin: EdgeInsets.only(top: isDesktopScreen ? 12 : 16), margin: EdgeInsets.only(top: isDesktopScreen ? 12 : 16),
child: Text( child: Text(
TIM_t("查看更多群成员"), TIM_t("查看更多群成员"),
style: TextStyle(color: theme.weakTextColor, fontSize: isDesktopScreen ? 12 : 14), style: TextStyle(
color: theme.weakTextColor,
fontSize: isDesktopScreen ? 12 : 14,
),
), ),
), ),
onTap: () async { onTap: () async {

View File

@ -1,6 +1,6 @@
name: tencent_cloud_chat_uikit name: tencent_cloud_chat_uikit
description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences. description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences.
version: 5.0.1 version: 5.0.2
homepage: https://trtc.io/products/chat?utm_source=gfs&utm_medium=link&utm_campaign=%E6%B8%A0%E9%81%93&_channel_track_key=k6WgfCKn homepage: https://trtc.io/products/chat?utm_source=gfs&utm_medium=link&utm_campaign=%E6%B8%A0%E9%81%93&_channel_track_key=k6WgfCKn
repository: https://github.com/TencentCloud/chat-uikit-flutter repository: https://github.com/TencentCloud/chat-uikit-flutter
documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html