Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
cometchat avatar

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-placement

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs6
repo stars70
Last updatedJune 23, 2026
Repositorycometchat/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

SKILL.mdMarkdownGitHub ↗

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-core for init, login, and rules
  • Read cometchat-flutter-v6-components for component names and props (when available)

---

Placement Recommendation

User intentRecommended patternComponents
Messaging appTab-based home (full screen)IndexedStack + CometChatConversations + Messages screen
Marketplace / platformNavigator.push from product pageSingle-thread messages screen
SaaS / dashboardModal or bottom sheetCometChatConversations in BottomSheet
Social / communityTab-based home with callsIndexedStack + Conversations + CallLogs + Users + Groups
Support / helpdeskFloating action button → modalFAB + showModalBottomSheet
Just exploringReplace home pageCometChatConversations 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:

  • IndexedStack keeps all tabs alive — no re-fetch when switching tabs
  • hideAppbar: true on all components — the parent Scaffold provides the AppBar
  • onItemTap extracts User/Group from conversation.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 screen
  • useSafeArea: true — respects notch/status bar
  • Inner Scaffold still needs resizeToAvoidBottomInset: false
  • Use MediaQuery.sizeOf(context) not MediaQuery.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: false on every Scaffold with CometChatMessageComposer
  • [ ] SafeArea(bottom: false) when using composer (it handles safe area internally)
  • [ ] Mutable _user/_group state copies, not widget.user/widget.group
  • [ ] onItemTap extracts User/Group from conversation.conversationWith
  • [ ] Protected groups handled (check group.hasJoined and group.type)
  • [ ] Incoming call handler mounted at app root level
  • [ ] Thread screen also has resizeToAvoidBottomInset: false
  • [ ] hideAppbar: true when parent provides its own AppBar
  • [ ] IndexedStack used for tab-based home (preserves tab state)

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.