flutter_advanced_chat_ui 1.0.0 copy "flutter_advanced_chat_ui: ^1.0.0" to clipboard
flutter_advanced_chat_ui: ^1.0.0 copied to clipboard

A highly customizable chat interface for Flutter with conversation list, message thread, search, reactions, replies, and typing indicators.

Flutter Advanced Chat UI #

A highly customizable chat interface for Flutter with builder-based customization and composable widgets. Build conversation lists and message threads with search, reactions, replies, typing indicators, and more.

Screenshots #

Conversation List Message Thread Reply Context
Conversation list with search and context menu Message thread with Alice Johnson Reply preview in input bar

Table of Contents #


Features #

ConversationList #

Feature Description
Search Filter conversations by name or custom logic via searchFilter
Unread badges Display unread count via trailingBuilder
Typing indicators Show "Typing..." in tile subtitle when isTyping is true
Pin & Mute Long-press menu with Pin, Mute, Delete options
Header & Footer Optional widgets above/below the scrollable list
Custom tiles Full control with tileBuilder for custom row layout

MessageThread #

Feature Description
Message bubbles Text, image, audio, and custom content types
Delivery status Pending, sent, delivered, read, failed with status icons
Reply to messages Long-press → Reply, shows reply preview in input bar
Reactions Add/remove emoji reactions (👍, ❤️, etc.) on messages
Typing indicator Animated three-dot indicator when other user is typing
Reply suggestions Quick-reply chips via replySuggestionsBuilder
Custom messages customMessageBuilder for custom message types

Installation #

Add to your pubspec.yaml:

dependencies:
  flutter_advanced_chat_ui: ^1.0.0

Then run:

flutter pub get

Quick Start #

1. Conversation List Screen #

import 'package:flutter_advanced_chat_ui/flutter_advanced_chat_ui.dart';

class ChatListScreen extends StatefulWidget {
  @override
  State<ChatListScreen> createState() => _ChatListScreenState();
}

class _ChatListScreenState extends State<ChatListScreen> {
  late final ConversationListController _controller;

  @override
  void initState() {
    super.initState();
    _controller = ConversationListController(
      initial: [
        Conversation(
          id: '1',
          title: 'Alice',
          participants: [Participant(id: 'a', displayName: 'Alice')],
          lastMessage: ChatMessage(
            id: 'm1',
            body: 'Hey there!',
            senderId: 'a',
            timestamp: DateTime.now(),
          ),
          unreadCount: 2,
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return ConversationList(
      controller: _controller,
      appBarTitle: 'Messages',
      searchHint: 'Search conversations',
      onConversationTap: (c) => _openChat(c),
      trailingBuilder: (conv) => conv.unreadCount > 0
        ? Container(
            padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
            decoration: BoxDecoration(
              color: Theme.of(context).colorScheme.primary,
              borderRadius: BorderRadius.circular(12),
            ),
            child: Text('${conv.unreadCount}', style: TextStyle(color: Colors.white)),
          )
        : null,
    );
  }

  void _openChat(Conversation conv) { /* Navigate to MessageThread */ }
}

2. Message Thread Screen #

final threadController = MessageThreadController(
  currentUser: Participant(id: 'me', displayName: 'Me'),
  others: conv.participants,
  initial: [
    ChatMessage(
      id: '1',
      body: 'Hello!',
      senderId: 'a',
      timestamp: DateTime.now().subtract(Duration(minutes: 5)),
    ),
  ],
);

MessageThread(
  controller: threadController,
  title: conv.title,
  subtitle: '@${conv.title.toLowerCase().replaceAll(' ', '_')}',
  avatarUrl: conv.avatarUrl,
  appBarActions: [
    IconButton(icon: Icon(Icons.videocam), onPressed: () {}),
    IconButton(icon: Icon(Icons.call), onPressed: () {}),
  ],
)

Architecture #

The package uses a controller-based architecture:

  • ConversationListController — Holds conversation list state, handles search, pin, mute, add/remove
  • MessageThreadController — Holds messages, typing state, reply suggestions, reactions

Both extend ChangeNotifier and notify listeners when state changes. Widgets listen via addListener and rebuild on updates.

Data flow:

Controller (state) → Widget (UI) → User action → Controller method → notifyListeners → Widget rebuild

Package Structure #

lib/
├── flutter_advanced_chat_ui.dart          # Main export
└── src/
    ├── controllers/
    │   ├── conversation_list_controller.dart
    │   └── message_thread_controller.dart
    ├── models/
    │   ├── conversation.dart
    │   ├── conversation_settings.dart
    │   ├── content_type.dart
    │   ├── delivery_state.dart
    │   ├── message.dart
    │   ├── participant.dart
    │   ├── reaction.dart
    │   └── reply_context.dart
    ├── theme/
    │   └── chat_theme.dart
    └── widgets/
        ├── conversation_list.dart
        ├── conversation_tile.dart
        ├── message_bubble.dart
        ├── message_input.dart
        └── message_thread.dart

ConversationList #

Parameters #

Parameter Type Default Description
controller ConversationListController required State controller
theme ChatTheme? ChatTheme.fromContext(context) Visual theme
appBarTitle String 'Messages' App bar title
searchHint String 'Search conversations' Search field placeholder
header Widget? null Widget above the list
footer Widget? null Widget below the list
onConversationTap void Function(Conversation)? null Called when a tile is tapped
onDelete void Function(Conversation)? null Called when delete is chosen
onPinChanged void Function(Conversation, bool)? null Called when pin status changes
onMuteChanged void Function(Conversation, bool)? null Called when mute status changes
tileBuilder Widget Function(BuildContext, Conversation)? null Custom tile widget
trailingBuilder Widget Function(Conversation)? null Trailing widget (e.g. badge)

ConversationListController Methods #

Method Description
applySearch(String query) Filter list by search term
clearSearch() Reset search filter
addConversation(Conversation c) Add conversation at top
removeConversation(String id) Remove by id
updateConversation(String id, updater) Update with function
setPin(String id, bool pinned) Pin or unpin
setMute(String id, bool muted) Mute or unmute
updateLastMessage(String id, ChatMessage? msg) Update last message
setTyping(String id, bool typing) Set typing indicator
setUnread(String id, int count) Set unread count

Custom Search Filter #

ConversationListController(
  initial: conversations,
  searchFilter: (query, items) {
    return items.where((c) =>
      c.title.toLowerCase().contains(query.toLowerCase()) ||
      c.participants.any((p) =>
        p.displayName.toLowerCase().contains(query.toLowerCase()))).toList();
  },
)

MessageThread #

Parameters #

Parameter Type Default Description
controller MessageThreadController required State controller
title String required Chat title (e.g. contact name)
subtitle String? null Subtitle (e.g. @username)
avatarUrl String? null Avatar image URL
theme ChatTheme? ChatTheme.fromContext(context) Visual theme
appBarActions List<Widget>? null Actions (call, video, etc.)
onBack VoidCallback? Navigator.pop Back button action
customMessageBuilder Widget Function(ChatMessage)? null Custom message widget
replySuggestionsBuilder Widget Function(List<String>)? null Quick-reply chips

MessageThreadController Methods #

Method Description
append(ChatMessage msg) Add message to thread
updateStatus(String id, DeliveryState status) Update delivery state
addReaction(String messageId, String emoji, String userId) Toggle reaction
setReplySuggestions(List<String> items) Show reply chips
isTyping (getter/setter) Typing indicator visibility

Message Types #

ContentType body usage Display
text Message text Text widget
image Image URL Image.network
audio "Voice message" placeholder
custom Use customMessageBuilder

Example: Image Message #

final msg = ChatMessage(
  id: '1',
  body: 'https://example.com/photo.jpg',
  senderId: 'me',
  timestamp: DateTime.now(),
  contentType: ContentType.image,
);

Example: Reply with Context #

// User long-presses a message → Reply → ReplyContext is set
// When sending:
controller.append(ChatMessage(
  id: '2',
  body: 'My reply',
  senderId: 'me',
  timestamp: DateTime.now(),
  replyTo: ReplyContext(
    messageId: '1',
    preview: 'Original message text',
    senderId: 'alice',
    senderName: 'Alice',
  ),
));

Models & Data #

Conversation #

Conversation(
  id: String,                    // Unique id
  title: String,                 // Display name
  participants: List<Participant>,
  avatarUrl: String?,             // Optional
  lastMessage: ChatMessage?,     // Optional
  unreadCount: int,              // Default 0
  isTyping: bool,                // Default false
  settings: ConversationSettings, // Default: isPinned=false, isMuted=false
)

Participant #

Participant(
  id: String,
  displayName: String,
  avatarUrl: String?,
  isOnline: bool,  // Default false
)

ChatMessage #

ChatMessage(
  id: String,
  body: String,
  senderId: String,
  timestamp: DateTime,
  contentType: ContentType,      // Default: text
  deliveryState: DeliveryState,  // Default: sent
  replyTo: ReplyContext?,        // Optional
  reactions: List<MessageReaction>, // Default: []
)

DeliveryState #

Value Description
pending Sending
sent Sent to server
delivered Delivered to recipient
read Read by recipient
failed Failed to send

ReplyContext #

ReplyContext(
  messageId: String,
  preview: String,
  senderId: String?,
  senderName: String?,
)

MessageReaction #

MessageReaction(
  emoji: String,   // e.g. '👍'
  userIds: List<String>,
)

Theme Customization #

ChatTheme Properties #

Property Description
background Scaffold background
surface Card/surface color
primary Primary accent
onBackground Text on background
onSurface Text on surface
onPrimary Text on primary
bubbleOutgoing Outgoing message bubble
bubbleIncoming Incoming message bubble
bubbleTextOutgoing Outgoing text color
bubbleTextIncoming Incoming text color
divider Divider color
error Error color

Usage #

// From context (uses Theme.of(context))
theme: ChatTheme.fromContext(context)

// Presets
theme: ChatTheme.light
theme: ChatTheme.dark

// Custom
theme: ChatTheme(
  primary: Color(0xFF6C63FF),
  bubbleOutgoing: Color(0xFF6C63FF),
  bubbleIncoming: Colors.grey.shade200,
)

Backend Integration #

The package is UI-only. Wire it to your backend:

  1. Conversation list — Fetch conversations from API, pass to ConversationListController(initial: [...]). Use addConversation, removeConversation, updateLastMessage when data changes.

  2. Messages — Fetch messages, pass to MessageThreadController(initial: [...]). Use append when new messages arrive. Use updateStatus when delivery status changes.

  3. Real-time — Listen to WebSocket/Stream, call controller methods when events arrive.

// Example: WebSocket message handler
socket.onMessage((data) {
  threadController.append(ChatMessage(
    id: data.id,
    body: data.text,
    senderId: data.senderId,
    timestamp: DateTime.parse(data.timestamp),
  ));
});

Example #

Run the example app:

cd example
flutter run

The example includes:

  • Conversation list with search
  • Dark/light theme toggle
  • Message thread with reply and reactions
  • Reply suggestions as chips
  • Unread badges on conversation tiles

License #

MIT License - see LICENSE for details.

1
likes
160
points
70
downloads

Publisher

verified publisherflexioninfotech.com

Weekly Downloads

A highly customizable chat interface for Flutter with conversation list, message thread, search, reactions, replies, and typing indicators.

Repository (GitHub)
View/report issues

Topics

#chat #ui #messaging

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flutter_advanced_chat_ui