cometchat-flutter-v6-messages
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, CometChat
What it does
CometChat Flutter UIKit — Messages
The messages screen is composed of three components: CometChatMessageHeader, CometChatMessageList, and CometChatMessageComposer.
Complete Messages Screen
Scaffold(
resizeToAvoidBottomInset: false, // REQUIRED — composer handles keyboard internally
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
| Prop | Type | Purpose |
|---|---|---|
user / group | User? / Group? | Target conversation (one required) |
goToMessageId | int? | Jump to specific message on load |
startFromUnreadMessages | bool | Start from unread position |
hideDeletedMessages | bool | Hide deleted message placeholders |
disableReceipts | bool | Hide read/delivered receipts |
disableReactions | bool | Hide reaction bar |
enableSwipeToReply | bool | Swipe gesture for reply |
hideDateSeparator | bool | Hide date headers |
textFormatters | List<CometChatTextFormatter> | Formatters for message text |
onThreadRepliesClick | Function(BaseMessage, BuildContext, {template}) | Thread navigation callback |
MessageListBloc Events
| Event | Purpose |
|---|---|
LoadMessages(conversationWith, conversationType) | Initial load |
LoadOlderMessages | Scroll up pagination |
LoadNewerMessages | Scroll 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
| Prop | Type | Purpose |
|---|---|---|
user / group | User? / Group? | Target conversation |
disableTypingEvents | bool | Stop sending typing indicators |
hideVoiceRecordingButton | bool | Hide audio recorder |
hideSendButton | bool | Hide send button |
hideAttachmentButton | bool | Hide attachment picker |
hideStickersButton | bool | Hide sticker panel |
disableMentions | bool | Disable @mentions |
hideBottomSafeArea | bool | Hide bottom safe area padding |
textFormatters | List<CometChatTextFormatter> | Text formatters |
Rich Text Formatting
The composer uses a WYSIWYG system (not the clean architecture module):
RichTextEditingController— span tracking, markdown rendering, format applicationSegmentComposerController— 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:
- At bottom: keyboard pushes list up (normal behavior)
- Scrolled up: list stays still, only composer moves
- Safe area: only added when keyboard is closed
This is why resizeToAvoidBottomInset: false is mandatory.
Message Bubbles
| Type | Widget | Key Features |
|---|---|---|
| Text | CometChatTextBubble | Rich text, links, markdown |
| Image | CometChatImageBubble | Local/network, GIF, HEIC fallback |
| Video | CometChatVideoBubble | Thumbnail, play overlay |
| Audio | CometChatAudioBubble | Waveform, play/pause, duration |
| File | CometChatFileBubble | Type icon, download, size |
| Deleted | CometChatDeletedBubble | "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(
resizeToAvoidBottomInset: false,
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
- Forgetting
resizeToAvoidBottomInset: falsecauses double keyboard compensation — the most common bug when integrating the messages screen. This applies to thread screens too — any Scaffold containing aCometChatMessageComposermust set it tofalse. textFormattersshould be the same list for bothCometChatMessageListandCometChatMessageComposerto 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. goToMessageIdloads messages around that ID, not from the beginning. The list may not have older messages loaded.- Keep mutable
_user/_groupcopies in your State class and update them from SDK listeners. Passingwidget.user/widget.groupdirectly means stale data after block/kick events. - The CometChat SDK has an
Actionclass (aBaseMessagesubclass used in group events like kick/ban/scope-change). It conflicts with Flutter's built-inActionwidget. If your messages screen uses SDK listener mixins that referenceAction, use an import alias:
This is especially common when mixingimport 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart' as cc; // Then use: cc.Action instead of ActionGroupListenerinto a messages screen State class.
Anti-Patterns
// ❌ WRONG — missing resizeToAvoidBottomInset
Scaffold(
body: Column(children: [
Expanded(child: CometChatMessageList(user: user)),
CometChatMessageComposer(user: user),
]),
)
// ❌ WRONG — thread screen with resizeToAvoidBottomInset: true
Scaffold(
resizeToAvoidBottomInset: true, // Double compensation!
body: Column(children: [
CometChatThreadedHeader(parentMessage: message, loggedInUser: user),
Expanded(child: CometChatMessageList(user: user, parentMessageId: message.id)),
CometChatMessageComposer(user: user, parentMessageId: message.id),
]),
)
// ✅ CORRECT — both messages and thread screens
Scaffold(
resizeToAvoidBottomInset: false,
body: Column(children: [
Expanded(child: CometChatMessageList(user: user)),
CometChatMessageComposer(user: user),
]),
)
// ❌ 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
- Scaffold has
resizeToAvoidBottomInset: false - Both list and composer have matching
textFormatters - Mutable
_user/_groupstate copies, notwidget.user/widget.group -
onThreadRepliesClicknavigates to thread screen withparentMessageId - Thread screen Scaffold also has
resizeToAvoidBottomInset: false(same rule as messages screen) - SDK listeners update
_user/_groupfor block/kick/scope changes - Colors from
CometChatThemeHelper, cached indidChangeDependencies() - If using SDK listener mixins with
Action, import UIKitas ccto avoid Flutter name conflict
Capabilities
Install
Quality
deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 27 github stars · SKILL.md body (10,672 chars)