Skip to main content
// User Listener — online/offline presence
CometChat.addUserListener("ID", UserListenerImpl());

// Message Listener — messages, typing, receipts, reactions
CometChat.addMessageListener("ID", MessageListenerImpl());

// Group Listener — member join/leave/kick/ban/scope changes
CometChat.addGroupListener("ID", GroupListenerImpl());

// Call Listener — incoming/outgoing call events
CometChat.addCallListener("ID", CallListenerImpl());

// Connection Listener — WebSocket connection state
CometChat.addConnectionListener("ID", ConnectionListenerImpl());

// Login Listener — authentication state changes
CometChat.addLoginListener("ID", LoginListenerImpl());

// Always clean up in dispose()
CometChat.removeUserListener("ID");
CometChat.removeMessageListener("ID");
CometChat.removeGroupListener("ID");
CometChat.removeCallListener("ID");
CometChat.removeConnectionListener("ID");
CometChat.removeLoginListener("ID");
Real-time listeners let you receive live events — messages, presence changes, group updates, and call signals — as they happen. The pattern is the same for all listener types:
  1. Create a class that uses the listener mixin with with
  2. Register a listener with a unique ID using addXListener()
  3. Handle events by overriding the callback methods
  4. Remove the listener with removeXListener() when it’s no longer needed
Each listener ID must be unique. Re-registering with the same ID replaces the previous listener. Always remove listeners in your widget’s dispose() method to prevent memory leaks.
Always remove listeners when they’re no longer needed (e.g., in dispose()). Failing to remove listeners can cause memory leaks and duplicate event handling.
CometChat provides 7 listener types:
  1. User Listener
  2. Group Listener
  3. Message Listener
  4. Call Listener
  5. Connection Listener
  6. Login Listener
  7. AI Assistant Listener

User Listener

Receive online/offline presence events for users.
MethodDescription
onUserOnline(User user)Triggered when a user comes online and is available to chat.
onUserOffline(User user)Triggered when a user goes offline.
class MyUserListener with UserListener {
  @override
  void onUserOnline(User user) {
    debugPrint("User online: ${user.name}");
  }

  @override
  void onUserOffline(User user) {
    debugPrint("User offline: ${user.name}");
  }
}

// Register
CometChat.addUserListener("UNIQUE_LISTENER_ID", MyUserListener());

// Remove when done
CometChat.removeUserListener("UNIQUE_LISTENER_ID");

Group Listener

Receive events when group members join, leave, are kicked/banned, or have their scope changed.
MethodDescription
onGroupMemberJoined(Action action, User joinedUser, Group joinedGroup)A user joined the group. All members receive this event.
onGroupMemberLeft(Action action, User leftUser, Group leftGroup)A member left the group. All members receive this event.
onGroupMemberKicked(Action action, User kickedUser, User kickedBy, Group kickedFrom)A member was kicked. All members including the kicked user receive this event.
onGroupMemberBanned(Action action, User bannedUser, User bannedBy, Group bannedFrom)A member was banned. All members including the banned user receive this event.
onGroupMemberUnbanned(Action action, User unbannedUser, User unbannedBy, Group unbannedFrom)A member was unbanned. All group members receive this event.
onGroupMemberScopeChanged(Action action, User updatedBy, User updatedUser, String scopeChangedTo, String scopeChangedFrom, Group group)A member’s scope was changed. All group members receive this event.
onMemberAddedToGroup(Action action, User addedby, User userAdded, Group addedTo)A user was added to the group. All members including the added user receive this event.
class MyGroupListener with GroupListener {
  @override
  void onGroupMemberJoined(Action action, User joinedUser, Group joinedGroup) {
    debugPrint("${joinedUser.name} joined ${joinedGroup.name}");
  }

  @override
  void onGroupMemberLeft(Action action, User leftUser, Group leftGroup) {
    debugPrint("${leftUser.name} left ${leftGroup.name}");
  }

  @override
  void onGroupMemberKicked(
      Action action, User kickedUser, User kickedBy, Group kickedFrom) {
    debugPrint("${kickedUser.name} was kicked by ${kickedBy.name}");
  }

  @override
  void onGroupMemberBanned(
      Action action, User bannedUser, User bannedBy, Group bannedFrom) {
    debugPrint("${bannedUser.name} was banned by ${bannedBy.name}");
  }

  @override
  void onGroupMemberUnbanned(
      Action action, User unbannedUser, User unbannedBy, Group unbannedFrom) {
    debugPrint("${unbannedUser.name} was unbanned by ${unbannedBy.name}");
  }

  @override
  void onGroupMemberScopeChanged(Action action, User updatedBy,
      User updatedUser, String scopeChangedTo, String scopeChangedFrom, Group group) {
    debugPrint("${updatedUser.name} scope changed to $scopeChangedTo");
  }

  @override
  void onMemberAddedToGroup(
      Action action, User addedby, User userAdded, Group addedTo) {
    debugPrint("${userAdded.name} was added to ${addedTo.name}");
  }
}

// Register
CometChat.addGroupListener("UNIQUE_LISTENER_ID", MyGroupListener());

// Remove when done
CometChat.removeGroupListener("UNIQUE_LISTENER_ID");

Message Listener

Receive events for incoming messages, typing indicators, read/delivery receipts, message edits/deletes, reactions, and moderation results.
MethodDescription
onTextMessageReceived(TextMessage textMessage)A text message was received.
onMediaMessageReceived(MediaMessage mediaMessage)A media message was received.
onCustomMessageReceived(CustomMessage customMessage)A custom message was received.
onTypingStarted(TypingIndicator typingIndicator)A user started typing in a conversation.
onTypingEnded(TypingIndicator typingIndicator)A user stopped typing in a conversation.
onMessagesDelivered(MessageReceipt messageReceipt)Messages were marked as delivered.
onMessagesRead(MessageReceipt messageReceipt)Messages were marked as read.
onMessagesDeliveredToAll(MessageReceipt messageReceipt)A group message was delivered to all members.
onMessagesReadByAll(MessageReceipt messageReceipt)A group message was read by all members.
onMessageEdited(BaseMessage message)A message was edited.
onMessageDeleted(BaseMessage message)A message was deleted.
onTransientMessageReceived(TransientMessage message)A transient (non-persistent) message was received.
onInteractiveMessageReceived(InteractiveMessage message)An interactive message was received.
onInteractionGoalCompleted(InteractionReceipt receipt)An interaction goal was achieved.
onMessageReactionAdded(ReactionEvent reactionEvent)A reaction was added to a message.
onMessageReactionRemoved(ReactionEvent reactionEvent)A reaction was removed from a message.
onMessageModerated(BaseMessage message)A message was processed by moderation (approved or disapproved).
onAIAssistantMessageReceived(AIAssistantMessage aiAssistantMessage)A persisted AI assistant reply was received.
onAIToolResultReceived(AIToolResultMessage aiToolResultMessage)A persisted AI tool result message was received.
onAIToolArgumentsReceived(AIToolArgumentMessage aiToolArgumentMessage)A persisted AI tool argument message was received.
class MyMessageListener with MessageListener {
  @override
  void onTextMessageReceived(TextMessage textMessage) {
    debugPrint("Text message: ${textMessage.text}");
  }

  @override
  void onMediaMessageReceived(MediaMessage mediaMessage) {
    debugPrint("Media message received");
  }

  @override
  void onCustomMessageReceived(CustomMessage customMessage) {
    debugPrint("Custom message received");
  }

  @override
  void onTypingStarted(TypingIndicator typingIndicator) {
    debugPrint("Typing started");
  }

  @override
  void onTypingEnded(TypingIndicator typingIndicator) {
    debugPrint("Typing ended");
  }

  @override
  void onMessagesDelivered(MessageReceipt messageReceipt) {
    debugPrint("Messages delivered");
  }

  @override
  void onMessagesRead(MessageReceipt messageReceipt) {
    debugPrint("Messages read");
  }

  @override
  void onMessagesDeliveredToAll(MessageReceipt messageReceipt) {
    debugPrint("Messages delivered to all");
  }

  @override
  void onMessagesReadByAll(MessageReceipt messageReceipt) {
    debugPrint("Messages read by all");
  }

  @override
  void onMessageEdited(BaseMessage message) {
    debugPrint("Message edited: ${message.id}");
  }

  @override
  void onMessageDeleted(BaseMessage message) {
    debugPrint("Message deleted: ${message.id}");
  }

  @override
  void onTransientMessageReceived(TransientMessage message) {
    debugPrint("Transient message received");
  }

  @override
  void onInteractiveMessageReceived(InteractiveMessage message) {
    debugPrint("Interactive message received");
  }

  @override
  void onInteractionGoalCompleted(InteractionReceipt receipt) {
    debugPrint("Interaction goal completed");
  }

  @override
  void onMessageReactionAdded(ReactionEvent reactionEvent) {
    debugPrint("Reaction added: ${reactionEvent.reaction}");
  }

  @override
  void onMessageReactionRemoved(ReactionEvent reactionEvent) {
    debugPrint("Reaction removed: ${reactionEvent.reaction}");
  }

  @override
  void onMessageModerated(BaseMessage message) {
    debugPrint("Message moderated: ${message.id}");
  }

  @override
  void onAIAssistantMessageReceived(AIAssistantMessage aiAssistantMessage) {
    debugPrint("AI Assistant message received");
  }

  @override
  void onAIToolResultReceived(AIToolResultMessage aiToolResultMessage) {
    debugPrint("AI Tool result received");
  }

  @override
  void onAIToolArgumentsReceived(AIToolArgumentMessage aiToolArgumentMessage) {
    debugPrint("AI Tool arguments received");
  }
}

// Register
CometChat.addMessageListener("UNIQUE_LISTENER_ID", MyMessageListener());

// Remove when done
CometChat.removeMessageListener("UNIQUE_LISTENER_ID");

Call Listener

Receive events for incoming and outgoing call state changes.
MethodDescription
onIncomingCallReceived(Call call)An incoming call was received.
onOutgoingCallAccepted(Call call)The outgoing call initiated by the logged-in user was accepted.
onOutgoingCallRejected(Call call)The outgoing call initiated by the logged-in user was rejected.
onIncomingCallCancelled(Call call)An incoming call was cancelled by the initiator.
onCallEndedMessageReceived(Call call)A call ended. The call object contains the final status and duration.
class MyCallListener with CallListener {
  @override
  void onIncomingCallReceived(Call call) {
    debugPrint("Incoming call from: ${call.sender?.name}");
  }

  @override
  void onOutgoingCallAccepted(Call call) {
    debugPrint("Outgoing call accepted");
  }

  @override
  void onOutgoingCallRejected(Call call) {
    debugPrint("Outgoing call rejected");
  }

  @override
  void onIncomingCallCancelled(Call call) {
    debugPrint("Incoming call cancelled");
  }

  @override
  void onCallEndedMessageReceived(Call call) {
    debugPrint("Call ended");
  }
}

// Register
CometChat.addCallListener("UNIQUE_LISTENER_ID", MyCallListener());

// Remove when done
CometChat.removeCallListener("UNIQUE_LISTENER_ID");

Connection Listener

Receive events when the WebSocket connection state changes.
MethodDescription
onConnected()SDK has an active connection to CometChat servers.
onConnecting()SDK is attempting to establish or re-establish a connection.
onDisconnected()SDK is disconnected due to network issues or other errors.
onFeatureThrottled()A feature has been throttled due to rate limiting.
onConnectionError(CometChatException error)A connection error occurred.
class MyConnectionListener with ConnectionListener {
  @override
  void onConnected() {
    debugPrint("Connected to CometChat");
  }

  @override
  void onConnecting() {
    debugPrint("Connecting...");
  }

  @override
  void onDisconnected() {
    debugPrint("Disconnected from CometChat");
  }

  @override
  void onFeatureThrottled() {
    debugPrint("Feature throttled");
  }

  @override
  void onConnectionError(CometChatException error) {
    debugPrint("Connection error: ${error.message}");
  }
}

// Register
CometChat.addConnectionListener("UNIQUE_LISTENER_ID", MyConnectionListener());

// Remove when done
CometChat.removeConnectionListener("UNIQUE_LISTENER_ID");

Login Listener

Receive events when the user’s authentication state changes.
MethodDescription
loginSuccess(User user)User logged in successfully. Provides the User object.
loginFailure(CometChatException e)Login failed. Provides a CometChatException.
logoutSuccess()User logged out successfully.
logoutFailure(CometChatException e)Logout failed. Provides a CometChatException.
class MyLoginListener with LoginListener {
  @override
  void loginSuccess(User user) {
    debugPrint("Login success: ${user.name}");
  }

  @override
  void loginFailure(CometChatException e) {
    debugPrint("Login failed: ${e.message}");
  }

  @override
  void logoutSuccess() {
    debugPrint("Logout success");
  }

  @override
  void logoutFailure(CometChatException e) {
    debugPrint("Logout failed: ${e.message}");
  }
}

// Register
CometChat.addLoginListener("UNIQUE_LISTENER_ID", MyLoginListener());

// Remove when done
CometChat.removeLoginListener("UNIQUE_LISTENER_ID");

AI Assistant Listener

Receive real-time streaming events from AI Agent runs. These events arrive as the agent processes a user’s message, before the final persisted messages arrive via the MessageListener.
MethodDescription
onAIAssistantEventReceived(AIAssistantBaseEvent aiAssistantBaseEvent)Triggered for each streaming event during an AI Agent run (run start, tool calls, text message streaming, run finished).
class MyAIAssistantListener with AIAssistantListener {
  @override
  void onAIAssistantEventReceived(AIAssistantBaseEvent aiAssistantBaseEvent) {
    debugPrint("AI Assistant event: ${aiAssistantBaseEvent.toString()}");
  }
}

// Register
CometChat.addAIAssistantListener("UNIQUE_LISTENER_ID", MyAIAssistantListener());

// Remove when done
CometChat.removeAIAssistantListener("UNIQUE_LISTENER_ID");

Using Listeners in a StatefulWidget

Here’s a complete example showing how to register and remove listeners within a Flutter widget lifecycle:
class ChatScreen extends StatefulWidget {
  const ChatScreen({super.key});

  @override
  State<ChatScreen> createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen>
    with MessageListener, ConnectionListener {
  static const String _listenerId = "CHAT_SCREEN_LISTENER";

  @override
  void initState() {
    super.initState();
    CometChat.addMessageListener(_listenerId, this);
    CometChat.addConnectionListener(_listenerId, this);
  }

  @override
  void dispose() {
    CometChat.removeMessageListener(_listenerId);
    CometChat.removeConnectionListener(_listenerId);
    super.dispose();
  }

  @override
  void onTextMessageReceived(TextMessage textMessage) {
    setState(() {
      // Update your message list
    });
  }

  @override
  void onConnected() {
    debugPrint("Connection restored");
  }

  @override
  void onDisconnected() {
    debugPrint("Connection lost");
  }

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(child: Text("Chat Screen")),
    );
  }
}

Next Steps

Receive Messages

Handle incoming messages in real-time

Typing Indicators

Show when users are typing

User Presence

Track online/offline status of users

Connection Status

Monitor SDK connection state changes