
Cometchat Flutter V6 Placement
- 6 installs
- 70 repo stars
- Updated June 23, 2026
- cometchat/cometchat-skills
Decide where to put CometChat in a Flutter app across tab home, pushed messages screen, modal, thread overlay, embedded panel, and floating widget patterns.
About
Covers five placement patterns for CometChat in a Flutter app with Scaffold and keyboard-handling guidance. A developer uses it to decide where chat lives given their app type.
- Placement recommendation table by app type
- Includes Scaffold config, resizeToAvoidBottomInset, and SafeArea patterns
Cometchat Flutter V6 Placement by the numbers
- 6 all-time installs (skills.sh)
- Ranked #806 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cometchat/cometchat-skills --skill cometchat-flutter-v6-placementAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 6 |
|---|---|
| repo stars | ★ 70 |
| Last updated | June 23, 2026 |
| Repository | cometchat/cometchat-skills ↗ |
What it does
Decide where to put CometChat in a Flutter app across tab home, pushed messages screen, modal, thread overlay, embedded panel, and floating widget patterns.
Files
CometChat Flutter UIKit — Placement
WHERE to put CometChat in your Flutter app. Five patterns, each with complete code.
Before using this skill:
- Read
cometchat-flutter-v6-corefor init, login, and rules - Read
cometchat-flutter-v6-componentsfor component names and props (when available)
---
Placement Recommendation
| User intent | Recommended pattern | Components |
|---|---|---|
| Messaging app | Tab-based home (full screen) | IndexedStack + CometChatConversations + Messages screen |
| Marketplace / platform | Navigator.push from product page | Single-thread messages screen |
| SaaS / dashboard | Modal or bottom sheet | CometChatConversations in BottomSheet |
| Social / community | Tab-based home with calls | IndexedStack + Conversations + CallLogs + Users + Groups |
| Support / helpdesk | Floating action button → modal | FAB + showModalBottomSheet |
| Just exploring | Replace home page | CometChatConversations as Scaffold body |
---
Pattern 1: Tab-Based Home (Most Common)
BottomNavigationBar with IndexedStack to preserve tab state. This is what master_app uses.
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _currentIndex,
children: [
// Tab 0: Conversations
CometChatConversations(
hideAppbar: true,
onItemTap: (conversation) => _openChat(context, conversation),
),
// Tab 1: Call Logs
CometChatCallLogs(hideAppbar: true),
// Tab 2: Users
CometChatUsers(
hideAppbar: true,
onItemTap: (_, user) => _pushMessages(context, user: user),
),
// Tab 3: Groups
CometChatGroups(
hideAppbar: true,
onItemTap: (_, group) => _pushMessages(context, group: group),
),
],
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (i) => setState(() => _currentIndex = i),
type: BottomNavigationBarType.fixed,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.chat_outlined), label: 'Chats'),
BottomNavigationBarItem(icon: Icon(Icons.call_outlined), label: 'Calls'),
BottomNavigationBarItem(icon: Icon(Icons.person_outline), label: 'Users'),
BottomNavigationBarItem(icon: Icon(Icons.people_outline), label: 'Groups'),
],
),
);
}
void _openChat(BuildContext context, Conversation conversation) {
final user = conversation.conversationWith is User
? conversation.conversationWith as User : null;
final group = conversation.conversationWith is Group
? conversation.conversationWith as Group : null;
_pushMessages(context, user: user, group: group);
}
void _pushMessages(BuildContext context, {User? user, Group? group}) {
Navigator.push(context, MaterialPageRoute(
builder: (_) => MessagesScreen(user: user, group: group),
));
}
}Key details:
IndexedStackkeeps all tabs alive — no re-fetch when switching tabshideAppbar: trueon all components — the parent Scaffold provides the AppBaronItemTapextractsUser/Groupfromconversation.conversationWith- Protected groups need a join/password screen before navigating to messages
---
Pattern 2: Messages Screen (Navigator.push)
The standard messages screen pushed from any conversation/user/group tap.
class MessagesScreen extends StatefulWidget {
final User? user;
final Group? group;
const MessagesScreen({super.key, this.user, this.group})
: assert(user != null || group != null);
@override
State<MessagesScreen> createState() => _MessagesScreenState();
}
class _MessagesScreenState extends State<MessagesScreen> {
late User? _user;
late Group? _group;
@override
void initState() {
super.initState();
_user = widget.user; // Mutable copy — updated by SDK listeners
_group = widget.group;
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false, // REQUIRED — composer handles keyboard
appBar: CometChatMessageHeader(
user: _user,
group: _group,
onBack: () => Navigator.pop(context),
),
body: SafeArea(
bottom: false, // Composer handles bottom safe area
child: Column(
children: [
Expanded(child: CometChatMessageList(user: _user, group: _group)),
CometChatMessageComposer(user: _user, group: _group),
],
),
),
);
}
}Critical Scaffold Rules
1. resizeToAvoidBottomInset: false — MANDATORY on any Scaffold with CometChatMessageComposer. The composer uses SliverSpacing internally to handle keyboard. Setting true causes double-compensation. 2. SafeArea(bottom: false) — The composer handles bottom safe area internally. 3. Mutable _user/_group — Keep mutable copies in State, not widget.user/widget.group. Update from SDK listeners for block/kick/scope changes.
---
Pattern 3: Thread Screen
Thread replies pushed from onThreadRepliesClick. Same Scaffold rules apply.
class ThreadScreen extends StatelessWidget {
final User? user;
final Group? group;
final BaseMessage message;
final CometChatMessageTemplate? template;
const ThreadScreen({
super.key, this.user, this.group,
required this.message, this.template,
});
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false, // REQUIRED — same rule as messages
appBar: CometChatMessageHeader(
user: user, group: group,
onBack: () => Navigator.pop(context),
),
body: SafeArea(
bottom: false,
child: Column(
children: [
CometChatThreadedHeader(
parentMessage: message,
loggedInUser: CometChatUIKit.loggedInUser!,
template: template,
),
Expanded(
child: CometChatMessageList(
user: user, group: group,
parentMessageId: message.id,
hideReplyInThreadOption: true,
),
),
CometChatMessageComposer(
user: user, group: group,
parentMessageId: message.id,
),
],
),
),
);
}
}Wire it from the messages screen:
CometChatMessageList(
onThreadRepliesClick: (message, ctx, {template}) {
Navigator.push(context, MaterialPageRoute(
builder: (_) => ThreadScreen(
user: _user, group: _group,
message: message, template: template,
),
));
},
)---
Pattern 4: Modal / Bottom Sheet Chat
Quick chat access from any screen without full navigation.
void _openChatModal(BuildContext context, User user) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
useSafeArea: true,
builder: (_) => SizedBox(
height: MediaQuery.sizeOf(context).height * 0.85,
child: Scaffold(
resizeToAvoidBottomInset: false, // Still required!
appBar: CometChatMessageHeader(
user: user,
onBack: () => Navigator.pop(context),
),
body: Column(
children: [
Expanded(child: CometChatMessageList(user: user)),
CometChatMessageComposer(user: user),
],
),
),
),
);
}Key details:
isScrollControlled: true— allows the sheet to be taller than half screenuseSafeArea: true— respects notch/status bar- Inner
Scaffoldstill needsresizeToAvoidBottomInset: false - Use
MediaQuery.sizeOf(context)notMediaQuery.of(context).size
---
Pattern 5: Embedded Panel
Chat embedded alongside other content (e.g., split view on tablets).
class SplitView extends StatelessWidget {
final User user;
const SplitView({super.key, required this.user});
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Row(
children: [
// Left: your app content
Expanded(flex: 3, child: YourProductPage()),
// Right: embedded chat
Expanded(
flex: 2,
child: Column(
children: [
CometChatMessageHeader(user: user),
Expanded(child: CometChatMessageList(user: user)),
CometChatMessageComposer(user: user),
],
),
),
],
),
);
}
}---
Pattern 6: Floating Action Button → Chat
Scaffold(
body: YourMainContent(),
floatingActionButton: FloatingActionButton(
onPressed: () => _openChatModal(context, supportUser),
child: const Icon(Icons.chat),
),
)---
Incoming Calls — Global Placement
Incoming call handling MUST be at the app root level, not per-screen. Use VoipCallHandler or mount the call overlay in MaterialApp.builder:
// In main.dart — before runApp
VoipCallHandler.instance.init();
// Or in MaterialApp builder for overlay approach
MaterialApp(
builder: (context, child) {
return Stack(children: [
child!,
// Global incoming call overlay
]);
},
)---
Anti-Patterns
// ❌ WRONG — resizeToAvoidBottomInset not set (defaults to true)
Scaffold(
body: Column(children: [
Expanded(child: CometChatMessageList(user: user)),
CometChatMessageComposer(user: user),
]),
)
// ❌ WRONG — passing widget.user directly (stale after block/kick)
CometChatMessageList(user: widget.user)
// ❌ WRONG — incoming call handler only on messages screen
// Calls ring on ALL screens, not just the one with call buttons
// ❌ WRONG — navigating to messages without extracting user/group
onItemTap: (conv) => Navigator.push(context, MaterialPageRoute(
builder: (_) => MessagesScreen(conversation: conv), // Wrong!
))
// ✅ CORRECT — extract user/group from conversation
onItemTap: (conv) {
final user = conv.conversationWith is User ? conv.conversationWith as User : null;
final group = conv.conversationWith is Group ? conv.conversationWith as Group : null;
Navigator.push(context, MaterialPageRoute(
builder: (_) => MessagesScreen(user: user, group: group),
));
}---
Checklist
- [ ]
resizeToAvoidBottomInset: falseon every Scaffold withCometChatMessageComposer - [ ]
SafeArea(bottom: false)when using composer (it handles safe area internally) - [ ] Mutable
_user/_groupstate copies, notwidget.user/widget.group - [ ]
onItemTapextractsUser/Groupfromconversation.conversationWith - [ ] Protected groups handled (check
group.hasJoinedandgroup.type) - [ ] Incoming call handler mounted at app root level
- [ ] Thread screen also has
resizeToAvoidBottomInset: false - [ ]
hideAppbar: truewhen parent provides its own AppBar - [ ]
IndexedStackused for tab-based home (preserves tab state)