OpenParable
Skip to content
← Back to skills

cometchat-flutter-messages

Development

Use when implementing the messages screen with CometChat Flutter UIKit v6. Covers CometChatMessageList, CometChatMessageComposer, CometChatMessageHeader, MessageListBloc, MessageComposerBloc, SliverSpacing, keyboard-aware spacing, rich text toolbar, CometChatTextBubble, CometChatImageBubble, CometChatVideoBubble, CometChatAudioBubble, CometChatFileBubble, CometChatMessageBubble, bubble factories, message templates, send message, edit message, delete message, reply, thread, reactions, receipts, typing indicators, mentions, markdown formatting, text formatters, scroll to bottom, unread messages, mark as read, goToMessageId, message options, swipe to reply, smart replies, or conversation starters. Also use when the user asks about building a chat screen, message input, or message display.

View on GitHub ↗
QUICK START

How to use this skill

Bring this guide into your coding agent with a prompt tailored to the tool you use.

  1. Open your project in Codex.
  2. Copy and paste the prompt below.
  3. Review files and risks before approving any action.
Prompt to paste
Inspect the Agent Skill at https://github.com/cometchat/cometchat-uikit-flutter/blob/HEAD/packages/cometchat_uikit/skills/cometchat-flutter-messages/SKILL.md and all supporting files. Report provenance, permissions, scripts, dependencies, and security risks. Propose an exact installation into .agents/skills/cometchat-flutter-messages, but wait for my approval before writing files or running scripts.

Copying this prompt does not install or run the skill. Review third-party files before use.

CometChat Flutter UIKit — Messages

The messages screen is composed of three components: CometChatMessageHeader, CometChatMessageList, and CometChatMessageComposer.

Complete Messages Screen

Scaffold(
  appBar: CometChatMessageHeader(
    user: user,
    group: group,
    onBack: () => Navigator.pop(context),
  ),
  body: Column(
    children: [
      Expanded(
        child: CometChatMessageList(
          user: user,
          group: group,
          textFormatters: [
            CometChatMentionsFormatter(user: user, group: group),
            MarkdownTextFormatter(),
            CometChatUrlFormatter(),
            CometChatPhoneNumberFormatter(),
            CometChatEmailFormatter(),
          ],
        ),
      ),
      CometChatMessageComposer(
        user: user,
        group: group,
        textFormatters: [
          CometChatMentionsFormatter(user: user, group: group),
          MarkdownTextFormatter(),
          CometChatUrlFormatter(),
        ],
      ),
    ],
  ),
)

CometChatMessageList

Displays messages with SliverAnimatedList, O(1) lookups, and keyboard-aware spacing.

Key Props

PropTypePurpose
user / groupUser? / Group?Target conversation (one required)
goToMessageIdint?Jump to specific message on load
startFromUnreadMessagesboolStart from unread position
hideDeletedMessagesboolHide deleted message placeholders
disableReceiptsboolHide read/delivered receipts
disableReactionsboolHide reaction bar
enableSwipeToReplyboolSwipe gesture for reply
hideDateSeparatorboolHide date headers
textFormattersList<CometChatTextFormatter>Formatters for message text
onThreadRepliesClickFunction(BaseMessage, BuildContext, {template})Thread navigation callback

MessageListBloc Events

EventPurpose
LoadMessages(conversationWith, conversationType)Initial load
LoadOlderMessagesScroll up pagination
LoadNewerMessagesScroll down pagination
MessageReceived(message)Real-time incoming message
MessageEdited(message)Real-time edit
MessageDeleted(message)Real-time delete
JumpToMessage(messageId)Scroll to specific message
MarkMessageAsRead(message)Mark as read
MarkMessageAsUnread(message)Mark as unread

MessageListState

MessageListState(
  status: MessageListStatus.loaded,  // initial, loading, loaded, empty, error
  messages: [...],
  isLoadingOlder: false,
  isLoadingNewer: false,
  hasMoreOlder: true,
  hasMoreNewer: false,
  unreadCount: 5,
  unreadMessageAnchor: message,
)

CometChatMessageComposer

Rich text input with formatting toolbar, attachments, mentions, and audio recording.

Key Props

PropTypePurpose
user / groupUser? / Group?Target conversation
disableTypingEventsboolStop sending typing indicators
hideVoiceRecordingButtonboolHide audio recorder
hideSendButtonboolHide send button
hideAttachmentButtonboolHide attachment picker
hideStickersButtonboolHide sticker panel
disableMentionsboolDisable @mentions
hideBottomSafeAreaboolHide bottom safe area padding
textFormattersList<CometChatTextFormatter>Text formatters

Rich Text Formatting

The composer uses a WYSIWYG system (not the clean architecture module):

  • RichTextEditingController — span tracking, markdown rendering, format application
  • SegmentComposerController — multi-segment (normal text + code blocks)
  • Toolbar buttons dispatch through cometchat_message_composer.dart

buildWhen Optimization

The composer uses buildWhen to prevent rebuilds during keyboard animation:

BlocConsumer<MessageComposerBloc, MessageComposerState>(
  buildWhen: (previous, current) =>
      previous.isEditMode != current.isEditMode ||
      previous.isReplyMode != current.isReplyMode ||
      previous.isRecordingMode != current.isRecordingMode,
  // ...
)

CometChatMessageHeader

Shows user/group info, typing indicators, and optional call buttons.

CometChatMessageHeader(
  user: user,
  group: group,
  onBack: () => Navigator.pop(context),
  hideVideoCallButton: false,
  hideVoiceCallButton: false,
  usersStatusVisibility: true,
  trailingView: (user, group, ctx) => [
    IconButton(icon: Icon(Icons.info_outline), onPressed: () { /* ... */ }),
  ],
  messageHeaderStyle: CometChatMessageHeaderStyle(
    backgroundColor: colorPalette.background1,
  ),
)

Keyboard-Aware Spacing

SliverSpacing handles keyboard interaction inside the message list:

  • At bottom: keyboard pushes list up (normal behavior)
  • Scrolled up: list stays still, only composer moves
  • Safe area: only added when keyboard is closed

Message Bubbles

TypeWidgetKey Features
TextCometChatTextBubbleRich text, links, markdown
ImageCometChatImageBubbleLocal/network, GIF, HEIC fallback
VideoCometChatVideoBubbleThumbnail, play overlay
AudioCometChatAudioBubbleWaveform, play/pause, duration
FileCometChatFileBubbleType icon, download, size
DeletedCometChatDeletedBubble"Message was deleted"

All bubble widgets accept optional colorPalette, spacing, typography params for the hybrid theme caching pattern.

Sending Messages

// Text message
await CometChatUIKit.sendTextMessage(
  TextMessage(
    text: 'Hello!',
    receiverUid: user.uid,
    receiverType: ReceiverTypeConstants.user,
  ),
);

// Media message
await CometChatUIKit.sendMediaMessage(
  MediaMessage(
    file: '/path/to/image.jpg',
    type: MessageTypeConstants.image,
    receiverUid: user.uid,
    receiverType: ReceiverTypeConstants.user,
  ),
);

// Custom message
await CometChatUIKit.sendCustomMessage(
  CustomMessage(
    type: 'location',
    receiverUid: user.uid,
    receiverType: ReceiverTypeConstants.user,
    customData: {'latitude': 37.7749, 'longitude': -122.4194},
  ),
);

Thread Replies

CometChatMessageList(
  onThreadRepliesClick: (message, ctx, {template}) {
    Navigator.push(context, MaterialPageRoute(
      builder: (_) => Scaffold(
        body: Column(
          children: [
            CometChatThreadedHeader(
              parentMessage: message,
              loggedInUser: CometChatUIKit.loggedInUser!,
            ),
            Expanded(child: CometChatMessageList(
              user: user, group: group,
              parentMessageId: message.id,
            )),
            CometChatMessageComposer(
              user: user, group: group,
              parentMessageId: message.id,
            ),
          ],
        ),
      ),
    ));
  },
)

Gotchas

  • textFormatters should be the same list for both CometChatMessageList and CometChatMessageComposer to ensure consistent rendering.
  • The rich text system has 3 implementations but only WYSIWYG is active at runtime. Bug fixes go in rich_text_editing_controller.dart, not the clean architecture module.
  • goToMessageId loads messages around that ID, not from the beginning. The list may not have older messages loaded.
  • Keep mutable _user/_group copies in your State class and update them from SDK listeners. Passing widget.user/widget.group directly means stale data after block/kick events.
  • The CometChat SDK has an Action class (a BaseMessage subclass used in group events like kick/ban/scope-change). It conflicts with Flutter's built-in Action widget. If your messages screen uses SDK listener mixins that reference Action, use an import alias:
    import 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart' as cc;
    // Then use: cc.Action instead of Action
    
    This is especially common when mixing GroupListener into a messages screen State class.

Anti-Patterns

// ❌ WRONG — passing immutable widget params to UIKit components
CometChatMessageList(user: widget.user) // Stale after block/unblock

// ✅ CORRECT — mutable state copy
late User? _user = widget.user;
// Update _user from SDK listeners
CometChatMessageList(user: _user)
// ❌ WRONG — different formatters for list and composer
CometChatMessageList(textFormatters: [MarkdownTextFormatter()])
CometChatMessageComposer(textFormatters: []) // Inconsistent rendering

// ✅ CORRECT — same formatters
final formatters = [CometChatMentionsFormatter(user: user), MarkdownTextFormatter()];
CometChatMessageList(textFormatters: formatters)
CometChatMessageComposer(textFormatters: formatters)

Checklist

  • Both list and composer have matching textFormatters
  • Mutable _user/_group state copies, not widget.user/widget.group
  • onThreadRepliesClick navigates to thread screen with parentMessageId
  • SDK listeners update _user/_group for block/kick/scope changes
  • Colors from CometChatThemeHelper, cached in didChangeDependencies()
  • If using SDK listener mixins with Action, import UIKit as cc to avoid Flutter name conflict