A comprehensive and customizable chat widget toolkit for Flutter applications. This package provides a complete chat interface with message handling, real-time updates, and extensive customization options.
- Complete Chat Interface: Ready-to-use chat widget with message display and input functionality
- Message Management: Support for different message types (sender/receiver) with state management
- Real-time Updates: Stream-based message handling for live chat experiences
- Customizable UI: Fully customizable message bubbles, profiles, and input fields
- Message States: Built-in support for loading, failed, and success message states
- Message Grouping: Automatic message organization and grouping
- Responsive Design: Auto-sizing components that adapt to different screen sizes
- Read-only Mode: Display-only chat interfaces for viewing conversations
- Scroll Management: Intelligent scroll behavior with new message notifications
Add this package to your pubspec.yaml
:
dependencies:
chat_toolkit: ^1.3.0
Then run:
flutter pub get
import 'package:flutter/material.dart';
import 'package:chat_toolkit/chat_toolkit.dart';
class ChatScreen extends StatefulWidget {
@override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
late ChatController chatController;
@override
void initState() {
super.initState();
chatController = ChatController();
// Add some sample messages
chatController.setMessages([
ReceiverMessage(
id: '1',
name: 'John',
timestamp: DateTime.now().toIso8601String(),
elements: [TextMessageElement(text: 'Hello! How are you?')],
),
SenderMessage(
name: 'You',
timestamp: DateTime.now().toIso8601String(),
elements: [TextMessageElement(text: 'I\'m doing great, thanks!')],
),
]);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Chat')),
body: Chat(
chatController: chatController,
configuration: ChatConfiguration(),
),
);
}
@override
void dispose() {
chatController.dispose();
super.dispose();
}
}
Chat(
chatController: chatController,
configuration: ChatConfiguration(
senderAlignment: ChatAlignment.end,
bubbleConfiguration: BubbleConfiguration(
maxWidth: 280,
profileBuilder: (context, name) => CustomProfileWidget(name),
senderBubbleBuilder: (context, child) => CustomBubble(
child: child,
color: Colors.blue,
),
receiverBubbleBuilder: (context, child) => CustomBubble(
child: child,
color: Colors.grey[200],
),
),
customInputField: (context, controller) => CustomInputField(
controller: controller,
),
),
)
class ImageElement extends MessageElement {
final String url;
final double? width;
final double? height;
const ImageElement({required this.url, this.width, this.height});
@override
Widget toWidget(BuildContext context) {
return Image.network(
url,width: width, height, height
);
}
}
// Adding new messages
chatController.addMessage(
SenderMessage(
name: 'You',
timestamp: DateTime.now().toIso8601String(),
elements: [TextMessageElement(text: 'New message')],
),
);
// Listening to message dispatch results
chatController.dispatchResultStream.listen((result) {
if (result.isSuccess) {
print('Message sent successfully');
} else {
print('Failed to send message: ${result.exception}');
}
});
// Listening to new received messages
chatController.newReceiveMessageStream.listen((message) {
print('New message received: ${message.id}');
});
Chat(
readOnly: true,
chatController: chatController,
configuration: ChatConfiguration(
readOnlyWidget: Container(
padding: EdgeInsets.all(16),
child: Text('This conversation is read-only'),
),
),
)
class ChatScreen extends StatefulWidget {
@override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
late ChatController chatController;
bool hasMoreMessages = true;
@override
void initState() {
super.initState();
chatController = ChatController();
}
Future<void> _loadMoreMessages() async {
if (!hasMoreMessages) return;
// Fetch older messages from your API
final olderMessages = await _fetchOlderMessages();
if (olderMessages.isEmpty) {
hasMoreMessages = false;
} else {
chatController.appendMessages(olderMessages);
}
}
Future<List<Message>> _fetchOlderMessages() async {
// Replace with your actual API call
return [
ReceiverMessage(
id: 'older_msg_1',
name: 'Support',
timestamp: DateTime.now().subtract(Duration(hours: 2)).toIso8601String(),
elements: [TextMessageElement(text: 'Previous message')],
),
];
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Chat(
chatController: chatController,
onScrollToTop: _loadMoreMessages,
),
);
}
}
// Sender message
final senderMessage = SenderMessage(
name: 'User Name',
timestamp: DateTime.now().toIso8601String(),
elements: [TextMessageElement(text: 'Hello world!')],
);
// Receiver message
final receiverMessage = ReceiverMessage(
id: 'unique_id',
name: 'Other User',
timestamp: DateTime.now().toIso8601String(),
elements: [TextMessageElement(text: 'Hi there!')],
);
// Message with loading state
final loadingMessage = SenderMessage(
name: 'User Name',
timestamp: DateTime.now().toIso8601String(),
elements: [TextMessageElement(text: 'Sending...')],
isLoading: true,
);
-
profileBuilder
: Custom profile widget builder -
senderBubbleBuilder
: Custom sender bubble builder -
receiverBubbleBuilder
: Custom receiver bubble builder -
timeBuilder
: Custom timestamp builder -
loadingWidget
: Custom loading indicator -
maxWidth
: Maximum bubble width
-
senderAlignment
: Message alignment (start/end) -
customInputField
: Custom input field widget -
newReceiveMessageNotificationBuilder
: New message notification builder -
bubbleConfiguration
: Bubble appearance settings -
newMessageScrollThreshold
: Scroll threshold for new messages
- Flutter SDK: >=3.24.0
- Dart SDK: ^3.5.0
-
gap
: ^3.0.1 - For spacing utilities -
intl
: ^0.19.0 - For internationalization -
uuid
: ^4.5.1 - For unique ID generation
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
For issues, feature requests, or questions, please visit our GitHub repository.