Skip to content

Commit 1fe99d4

Browse files
feat: ✨ Add Menu Option for ChatViewList
1 parent 9a4c7f5 commit 1fe99d4

File tree

8 files changed

+184
-13
lines changed

8 files changed

+184
-13
lines changed

example/lib/chat_view_list_screen.dart

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,31 @@ class _ChatViewListScreenState extends State<ChatViewListScreen> {
4343
),
4444
loadMoreChats: () async =>
4545
await Future.delayed(const Duration(seconds: 2)),
46+
menuConfig: ChatMenuConfig(
47+
enabled: true,
48+
muteStatusCallback: (result) {
49+
controller?.updateChat(
50+
result.chat.id,
51+
(previousChat) => previousChat.copyWith(
52+
settings: previousChat.settings.copyWith(
53+
muteStatus: result.status,
54+
),
55+
),
56+
);
57+
Navigator.of(context).pop();
58+
},
59+
pinStatusCallback: (result) {
60+
controller?.updateChat(
61+
result.chat.id,
62+
(previousChat) => previousChat.copyWith(
63+
settings: previousChat.settings.copyWith(
64+
pinStatus: result.status,
65+
),
66+
),
67+
);
68+
Navigator.of(context).pop();
69+
},
70+
),
4671
config: ChatViewListConfig(
4772
enablePagination: true,
4873
loadMoreConfig: const LoadMoreConfig(),
@@ -71,9 +96,6 @@ class _ChatViewListScreenState extends State<ChatViewListScreen> {
7196
),
7297
);
7398
},
74-
onLongPress: (chat) {
75-
debugPrint('Long pressed on chat: ${chat.name}');
76-
},
7799
),
78100
searchConfig: ChatViewListSearchConfig(
79101
textEditingController: TextEditingController(),
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import 'package:flutter/cupertino.dart';
2+
3+
import '../../../values/enumeration.dart';
4+
import '../../../values/typedefs.dart';
5+
6+
class ChatMenuConfig {
7+
const ChatMenuConfig({
8+
this.enabled = true,
9+
this.actions,
10+
this.textStyle,
11+
this.menuBuilder,
12+
this.muteStatusCallback,
13+
this.pinStatusCallback,
14+
this.muteStatusTrailingIcon,
15+
this.pinStatusTrailingIcon,
16+
});
17+
18+
/// Whether the context menu is enabled.
19+
///
20+
/// Defaults to `true`.
21+
///
22+
/// if set to `false`, the default or custom context menu
23+
/// will not be shown.
24+
final bool enabled;
25+
26+
/// Custom text style for the menu items.
27+
final TextStyle? textStyle;
28+
29+
/// Custom actions to be added to the context menu.
30+
///
31+
/// by default, it will add the mute and pin actions
32+
/// if callbacks are provided.
33+
final MenuActionBuilder? actions;
34+
35+
/// Custom menu builder to create the context menu.
36+
final MenuBuilderCallback? menuBuilder;
37+
38+
/// Callback for mute status changes.
39+
///
40+
/// In this callback, new mute status along with the chat
41+
/// is provided.
42+
final ChatStatusCallback<MuteStatus>? muteStatusCallback;
43+
44+
/// Callback for pin status changes.
45+
final ChatStatusCallback<PinStatus>? pinStatusCallback;
46+
47+
/// Custom trailing icon for mute status menu item.
48+
final StatusTrailingIcon<MuteStatus>? muteStatusTrailingIcon;
49+
50+
/// Custom trailing icon for pin status menu item.
51+
final StatusTrailingIcon<PinStatus>? pinStatusTrailingIcon;
52+
}

lib/src/models/config_models/chat_view_list/chat_view_list_tile_config.dart

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ class ChatViewListTileConfig {
5252
this.userNameTextStyle,
5353
this.lastMessageTextStyle,
5454
this.onTap,
55-
this.onLongPress,
5655
this.lastMessageTileBuilder,
5756
this.userNameBuilder,
5857
this.trailingBuilder,
@@ -109,9 +108,6 @@ class ChatViewListTileConfig {
109108
/// Callback function that is called when a user taps on a chat item.
110109
final ValueSetter<ChatViewListItem>? onTap;
111110

112-
/// Callback function that is called when the user long presses on a chat item.
113-
final ValueSetter<ChatViewListItem>? onLongPress;
114-
115111
/// Configuration for the user avatar in the chat list.
116112
final UserAvatarConfig userAvatarConfig;
117113

lib/src/models/config_models/chat_view_list/models.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* SOFTWARE.
2121
*/
2222
export 'chat_settings.dart';
23+
export 'chat_menu_config.dart';
2324
export 'chat_view_list_config.dart';
2425
export 'chat_view_list_tile_config.dart';
2526
export 'last_message_time_config.dart';

lib/src/values/typedefs.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,10 @@ typedef ChatSorter = int Function(
136136
ChatViewListItem chat1,
137137
ChatViewListItem chat2,
138138
);
139+
typedef MenuWidgetCallback = Widget Function(ChatViewListItem chat);
140+
typedef MenuBuilderCallback = Widget Function(
141+
BuildContext context,
142+
ChatViewListItem chat,
143+
Widget child,
144+
);
145+
typedef MenuActionBuilder = List<Widget> Function(ChatViewListItem chat);
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import 'package:flutter/cupertino.dart';
2+
import 'package:flutter/material.dart';
3+
4+
import '../../models/models.dart';
5+
import '../../values/enumeration.dart';
6+
7+
class ChatListTileContextMenu extends StatelessWidget {
8+
const ChatListTileContextMenu({
9+
required this.chat,
10+
required this.child,
11+
required this.config,
12+
required this.chatTileColor,
13+
super.key,
14+
});
15+
16+
final Widget child;
17+
final ChatViewListItem chat;
18+
final ChatMenuConfig config;
19+
final Color chatTileColor;
20+
21+
@override
22+
Widget build(BuildContext context) {
23+
if (!config.enabled) return child;
24+
25+
final menu = config.menuBuilder?.call(context, chat, child);
26+
if (menu != null) return menu;
27+
28+
final newMuteStatus = switch (chat.settings.muteStatus) {
29+
MuteStatus.muted => MuteStatus.unmute,
30+
MuteStatus.unmute => MuteStatus.muted,
31+
};
32+
final newPinStatus = switch (chat.settings.pinStatus) {
33+
PinStatus.pinned => PinStatus.unpinned,
34+
PinStatus.unpinned => PinStatus.pinned,
35+
};
36+
37+
final actions = <Widget>[
38+
...?config.actions?.call(chat),
39+
if (config.muteStatusCallback != null)
40+
CupertinoContextMenuAction(
41+
trailingIcon: config.muteStatusTrailingIcon?.call(
42+
newMuteStatus,
43+
) ??
44+
newMuteStatus.iconData,
45+
child: Text(
46+
newMuteStatus.menuName,
47+
style: config.textStyle,
48+
),
49+
onPressed: () => config.muteStatusCallback?.call((
50+
chat: chat,
51+
status: newMuteStatus,
52+
)),
53+
),
54+
if (config.pinStatusCallback != null)
55+
CupertinoContextMenuAction(
56+
trailingIcon: config.pinStatusTrailingIcon?.call(
57+
newPinStatus,
58+
) ??
59+
newPinStatus.iconData,
60+
child: Text(
61+
newPinStatus.menuName,
62+
style: config.textStyle,
63+
),
64+
onPressed: () => config.pinStatusCallback?.call((
65+
chat: chat,
66+
status: newPinStatus,
67+
)),
68+
),
69+
];
70+
71+
return actions.isEmpty
72+
? child
73+
: CupertinoContextMenu.builder(
74+
builder: (_, __) => Material(
75+
color: chatTileColor,
76+
child: child,
77+
),
78+
actions: actions,
79+
);
80+
}
81+
}

lib/src/widgets/chat_view_list/chat_view_list.dart

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,20 @@ import 'package:flutter/material.dart';
2525

2626
import '../../controller/chat_list_view_controller.dart';
2727
import '../../models/chat_view_list_item.dart';
28+
import '../../models/config_models/chat_view_list/chat_menu_config.dart';
2829
import '../../models/config_models/chat_view_list/chat_view_list_config.dart';
2930
import '../../models/config_models/chat_view_list/load_more_config.dart';
3031
import '../../values/typedefs.dart';
3132
import '../../utils/constants/constants.dart';
33+
import 'chat_list_tile_context_menu.dart';
3234
import 'chat_view_list_item_tile.dart';
3335
import 'search_text_field.dart';
3436

3537
class ChatViewList extends StatefulWidget {
3638
const ChatViewList({
3739
required this.controller,
3840
this.config = const ChatViewListConfig(),
41+
this.menuConfig = const ChatMenuConfig(),
3942
this.scrollViewKeyboardDismissBehavior =
4043
ScrollViewKeyboardDismissBehavior.onDrag,
4144
this.chatBuilder,
@@ -75,6 +78,9 @@ class ChatViewList extends StatefulWidget {
7578
/// Defaults to `ScrollViewKeyboardDismissBehavior.onDrag`.
7679
final ScrollViewKeyboardDismissBehavior? scrollViewKeyboardDismissBehavior;
7780

81+
/// Callback to provide a widget for the menu in the chat list.
82+
final ChatMenuConfig menuConfig;
83+
7884
@override
7985
State<ChatViewList> createState() => _ChatViewListState();
8086
}
@@ -128,11 +134,18 @@ class _ChatViewListState extends State<ChatViewList> {
128134
}
129135

130136
final chat = chats[itemIndex];
131-
return widget.chatBuilder?.call(context, chat) ??
132-
ChatViewListItemTile(
133-
chat: chat,
134-
config: widget.config.tileConfig,
135-
);
137+
return ChatListTileContextMenu(
138+
key: ValueKey(chat.id),
139+
chat: chat,
140+
config: widget.menuConfig,
141+
chatTileColor: widget.config.backgroundColor,
142+
child: widget.chatBuilder
143+
?.call(context, chat) ??
144+
ChatViewListItemTile(
145+
chat: chat,
146+
config: widget.config.tileConfig,
147+
),
148+
);
136149
},
137150
),
138151
);

lib/src/widgets/chat_view_list/chat_view_list_item_tile.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ class ChatViewListItemTile extends StatelessWidget {
5959
final hasTimeSubChild = isPinned || isMuted || showUnreadCount;
6060
return GestureDetector(
6161
behavior: HitTestBehavior.opaque,
62-
onLongPress: () => config.onLongPress?.call(chat),
6362
onTap: () {
6463
FocusManager.instance.primaryFocus?.unfocus();
6564
config.onTap?.call(chat);

0 commit comments

Comments
 (0)