FL Firebase Chat
A lightweight, reusable Flutter chat package with Firebase backend support and advanced group chat features.
Features
- ✅ User Authentication - Simple username-based login system
- ✅ Direct Messages - 1-on-1 private messaging
- ✅ Group Chat - Create and manage group conversations
- ✅ Real-time Messaging - Live message updates
- ✅ User Management - Online status, user search
- ✅ Message Status - Sent, delivered indicators
- ✅ Customizable UI - Custom message bubbles, avatars, themes
- ✅ Admin Controls - Group admin privileges
- ✅ System Messages - Automated group notifications
- ✅ Custom Avatar Builder - Create your own avatar designs
- ✅ Thread/Reply System - Reply to messages and view conversation threads
- ✅ Message Reactions - React to messages with emojis
- ✅ Media Sharing - Share images, videos, audio files, and documents
Quick Start
1. Installation
Add to your pubspec.yaml
:
dependencies:
fl_firebase_chat: ^0.1.0
2. Firebase Setup
Set up Firebase in your Flutter project and add the required plugins to pubspec.yaml
:
dependencies:
firebase_core: ^4.0.0
firebase_auth: ^6.0.0
cloud_firestore: ^6.0.0
firebase_storage: ^12.0.0 # For media file storage
path: ^1.9.0 # For file path operations
3. Initialize Firebase
import 'package:firebase_core/firebase_core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
4. Basic Usage
import 'package:fl_firebase_chat/fl_firebase_chat.dart';
// Login user
final user = await FlFirebaseChat.login('username', displayName: 'John Doe');
// Show chat list
ChatListScreen()
// Show user list
UserListScreen()
// Direct chat
ChatScreen(
chatId: chatId,
otherUser: otherUser,
)
// Group chat
ChatScreen(
chatId: chatId,
groupChat: groupChat,
)
Group Chat Features
Creating a Group
final chatService = ChatService();
final groupId = await chatService.createGroupChat(
groupName: 'Team Discussion',
groupDescription: 'Weekly team sync',
participantIds: ['user1', 'user2', 'user3'],
);
Group Management (Admin Only)
// Add users to group
await chatService.addUsersToGroup(
chatId: groupId,
userIds: ['user4', 'user5'],
);
// Remove users from group
await chatService.removeUsersFromGroup(
chatId: groupId,
userIds: ['user2'],
);
// Update group info
await chatService.updateGroupInfo(
chatId: groupId,
groupName: 'New Group Name',
groupDescription: 'Updated description',
);
Leave Group
await chatService.leaveGroup(groupId);
Custom Avatar Builder
Create your own avatar designs for group chats:
ChatScreen(
chatId: chatId,
groupChat: groupChat,
// Custom avatar builder
avatarBuilder: (context, user, message, isCurrentUser) {
return Container(
width: 32,
height: 32,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: isCurrentUser
? [Colors.blue.shade300, Colors.blue.shade600]
: [Colors.purple.shade300, Colors.purple.shade600],
),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: user?.photoUrl != null
? ClipOval(
child: Image.network(
user!.photoUrl!,
width: 32,
height: 32,
fit: BoxFit.cover,
),
)
: Center(
child: Text(
user?.displayName.isNotEmpty == true
? user!.displayName[0].toUpperCase()
: '?',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
);
},
)
Avatar Display Rules
- Group Chats: Avatars are always shown for all messages
- Direct Messages: Avatars are hidden by default
- Custom Control: Use
showAvatarsInDirectMessages: true
to override
ChatScreen(
chatId: chatId,
otherUser: otherUser,
showAvatarsInDirectMessages: true, // Show avatars in direct messages
)
Customization Options
Message Bubbles
ChatScreen(
chatId: chatId,
groupChat: groupChat,
sentMessageDecoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(16),
),
receivedMessageDecoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(16),
),
)
Text Styles
ChatScreen(
chatId: chatId,
groupChat: groupChat,
sentMessageTextStyle: TextStyle(color: Colors.white, fontSize: 16),
receivedMessageTextStyle: TextStyle(color: Colors.black87, fontSize: 16),
timestampTextStyle: TextStyle(color: Colors.grey, fontSize: 11),
)
Chat List Customization
ChatListScreen(
title: 'My Chats',
chatTitleStyle: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
lastMessageStyle: TextStyle(color: Colors.grey[600], fontSize: 14),
emptyStateWidget: Center(
child: Text('No chats yet! Start a conversation.'),
),
)
Widgets Reference
Core Widgets
ChatListScreen
- Displays all user chatsUserListScreen
- Shows available usersChatScreen
- Main chat interfaceCreateGroupScreen
- Group creation formMediaPicker
- Media selection and preview widgetQuickMediaPicker
- Quick media selection buttons
Models
User
- User information and statusChat
- Chat room data and participantsMessage
- Individual message data with media supportMessageReaction
- Reaction data with user trackingMessageMedia
- Media attachment informationMediaType
- Enum for media file types
Services
ChatService
- Core chat operations with media supportAuthService
- User authenticationStorageService
- Firebase Storage file management
Advanced Features
Message Reactions
Users can react to messages with emojis:
// Reactions are automatically handled by the ChatScreen widget
// Users can:
// - Long press a message -> "Tepki Ver" -> Select emoji
// - Tap on existing reactions to toggle their own reaction
// - Long press reactions to see who reacted
Available reaction emojis: 👍 ❤️ 😄 😮 😢 😡 👏 🔥 🎉 💯
Message Read Status
Track read and delivery status for all messages:
final chatService = ChatService();
// Mark a message as read
await chatService.markMessageAsRead(
chatId: 'chat_id',
messageId: 'message_id',
);
// Mark a message as delivered
await chatService.markMessageAsDelivered(
chatId: 'chat_id',
messageId: 'message_id',
);
// Mark all messages as read up to a timestamp
await chatService.markMessagesAsReadUpTo(
chatId: 'chat_id',
upToTimestamp: DateTime.now(),
);
// Get unread message count
final unreadCount = await chatService.getUnreadMessageCount(
chatId: 'chat_id',
);
// Get read status for a specific message
final readBy = await chatService.getMessageReadStatus(
chatId: 'chat_id',
messageId: 'message_id',
);
// Auto-mark messages as delivered (called automatically)
await chatService.autoMarkAsDelivered(chatId: 'chat_id');
Automatic Behavior:
- Messages are automatically marked as delivered when user opens a chat
- Messages are automatically marked as read when user scrolls to bottom
- Visual indicators show message status with appropriate icons and colors
Thread/Reply System
Reply to specific messages and view conversation threads:
// Thread features are built into ChatScreen:
// - Long press a message -> "Yanıtla" to reply
// - Long press a reply -> "Thread'i Görüntüle" to see all replies
// - Reply preview shows above message input
Message Copy
Copy message content to clipboard:
// Message copy is built into ChatScreen:
// - Long press a message -> "Kopyala" to copy
// - Text messages: copies the text content
// - Media messages: copies media info (e.g., "[Image: photo.jpg]")
// - Mixed messages: copies both text and media info
// - Success/error feedback via SnackBar
Features:
- Smart content detection (text + media)
- Formatted output for different media types
- User feedback with success/error messages
- Automatic clipboard integration
Message Deletion (Soft Delete)
Delete messages while preserving chat history:
// Message deletion is built into ChatScreen:
// - Long press a message -> "Sil" to delete (only for sender's own messages)
// - Shows confirmation dialog before deletion
// - Deleted messages show as "Bu mesaj silindi" with delete icon
// - Original content is permanently removed for privacy
Features:
- Privacy-focused: Original content and media are permanently cleared
- Ownership control: Only message sender can delete their own messages
- Visual feedback: Deleted messages show with special styling
- Confirmation dialog: Prevents accidental deletions
- Real-time updates: Deleted messages update instantly for all users
- Chat history preserved: Message structure remains for conversation flow
Media Sharing
Share images, videos, audio files, and documents:
// Media sharing is built into ChatScreen with attach button
// Users can:
// - Tap attach button (📎) to open media picker
// - Select from camera, gallery, or files
// - Send multiple files at once
// - View media in message bubbles with previews
// Programmatic media sending:
final chatService = ChatService();
await chatService.sendMediaMessage(
chatId: chatId,
files: [File('/path/to/image.jpg'), File('/path/to/document.pdf')],
text: 'Check out these files!',
onUploadProgress: (progress, fileIndex) {
print('File $fileIndex: ${progress * 100}% uploaded');
},
);
Supported media types:
- Images: JPG, PNG, GIF, WebP, BMP
- Videos: MP4, AVI, MOV, WMV, WebM, MKV
- Audio: MP3, WAV, FLAC, AAC, OGG, M4A
- Files: PDF, DOC, XLS, PPT, TXT, ZIP, RAR
System Messages
Automatic notifications for group activities:
- User joined/left group
- Group name/description changed
- Admin rights transferred
Message Status
- Sending - Message being sent (loading indicator)
- Sent - Message successfully sent (single checkmark)
- Delivered - Message delivered to all recipients (double checkmark, gray)
- Read - Message read by all recipients (double checkmark, blue)
- Failed - Message failed to send (tap to retry)
Online Indicators
Track user presence and last seen timestamps.
Search Users
final chatService = ChatService();
// Search users by name or email
final users = chatService.searchUsers('john').listen((users) {
// Handle search results
});
Firebase Security Rules
Add these Firestore security rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Users can read/write their own user document
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
// Users can read other users (for chat functionality)
match /users/{userId} {
allow read: if request.auth != null;
}
// Chat participants can read/write chat documents
match /chats/{chatId} {
allow read, write: if request.auth != null &&
request.auth.uid in resource.data.participants;
// Allow users to update their own lastReadTimestamps
allow update: if request.auth != null &&
request.auth.uid in resource.data.participants &&
request.writeFields.hasOnly(['lastReadTimestamps']) &&
request.resource.data.lastReadTimestamps[request.auth.uid] is timestamp;
}
// Chat participants can read/write messages
match /chats/{chatId}/messages/{messageId} {
allow read, write: if request.auth != null &&
exists(/databases/$(database)/documents/chats/$(chatId)) &&
request.auth.uid in get(/databases/$(database)/documents/chats/$(chatId)).data.participants;
// Allow users to update read/delivery status for their own messages
allow update: if request.auth != null &&
exists(/databases/$(database)/documents/chats/$(chatId)) &&
request.auth.uid in get(/databases/$(database)/documents/chats/$(chatId)).data.participants &&
(
// Allow updating readBy for current user
(request.writeFields.hasOnly(['readBy']) &&
request.resource.data.readBy[request.auth.uid] is timestamp) ||
// Allow updating deliveredTo for current user
(request.writeFields.hasOnly(['deliveredTo']) &&
request.resource.data.deliveredTo[request.auth.uid] is timestamp) ||
// Allow updating both readBy and deliveredTo
(request.writeFields.hasOnly(['readBy', 'deliveredTo']) &&
request.resource.data.readBy[request.auth.uid] is timestamp &&
request.resource.data.deliveredTo[request.auth.uid] is timestamp) ||
// Allow message sender to delete their own message (soft delete)
(request.writeFields.hasOnly(['text', 'media', 'deleted', 'deletedAt']) &&
resource.data.senderId == request.auth.uid &&
request.resource.data.deleted == true &&
request.resource.data.deletedAt is timestamp)
);
}
}
}
Add these Firebase Storage security rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
// Chat media files - only chat participants can access
match /chats/{chatId}/{mediaType}/{fileName} {
allow read, write: if request.auth != null &&
request.auth.uid in firestore.get(/databases/(default)/documents/chats/$(chatId)).data.participants;
}
// User profile pictures
match /users/{userId}/profile/{fileName} {
allow read: if request.auth != null;
allow write: if request.auth != null && request.auth.uid == userId;
}
}
}
License
This project is licensed under the MIT License.