chat_plugin 1.0.0
chat_plugin: ^1.0.0 copied to clipboard
A flexible, feature-rich chat plugin for Flutter applications that provides real-time messaging capabilities using Socket.IO.
example/lib/main.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:chat_plugin/chat_plugin.dart';
import 'package:http/http.dart' as http;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize the chat plugin with a custom configuration
await ChatPlugin.initialize(
config: ChatConfig(
apiUrl: 'https://your-chat-server.com',
enableTypingIndicators: true,
enableReadReceipts: true,
enableOnlineStatus: true,
autoMarkAsRead: true,
connectionTimeout: 15,
maxReconnectionAttempts: 3,
chatRoomRefreshInterval: 30,
// You can provide initial user credentials if available
userId: null, // Will be set after login
token: null, // Will be set after login
// Additional headers for API requests
additionalHeaders: {
'App-Version': '1.0.0',
'Platform': 'flutter',
},
// Custom socket parameters
socketParams: {
'transports': ['websocket'],
'autoConnect': false,
}
),
);
// Set up custom API handlers
setupCustomApiHandlers();
// Set up custom socket events
setupCustomSocketEvents();
runApp(MyApp());
}
void setupCustomApiHandlers() {
// Create custom API handlers for chat operations
final apiHandlers = ChatApiHandlers(
// Custom implementation for loading messages
loadMessagesHandler: ({int page = 1, int limit = 20, String searchText = ""}) async {
final chatService = ChatPlugin.chatService;
final userId = ChatConfig.instance.userId;
final token = ChatConfig.instance.token;
final receiverId = chatService.receiverId;
if (userId == null || token == null || receiverId.isEmpty) {
throw Exception('Not authenticated or no receiver selected');
}
// Custom implementation for fetching messages
final response = await http.get(
Uri.parse('https://your-custom-api.com/messages?sender=$userId&receiver=$receiverId&page=$page&limit=$limit'),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
final List<dynamic> data = jsonDecode(response.body);
return data.map((msg) => ChatMessage.fromMap(msg, userId)).toList();
} else {
throw Exception('Failed to load messages: ${response.statusCode}');
}
},
// Custom implementation for loading chat rooms
loadChatRoomsHandler: () async {
final userId = ChatConfig.instance.userId;
final token = ChatConfig.instance.token;
if (userId == null || token == null) {
throw Exception('Not authenticated');
}
// Custom implementation for fetching chat rooms
final response = await http.get(
Uri.parse('https://your-custom-api.com/chat-rooms?userId=$userId'),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
final List<dynamic> data = jsonDecode(response.body);
return data.map((room) => ChatRoom.fromMap(room)).toList();
} else {
throw Exception('Failed to load chat rooms: ${response.statusCode}');
}
},
// Custom implementation for sending messages
sendMessageHandler: (String text) async {
final chatService = ChatPlugin.chatService;
final userId = ChatConfig.instance.userId;
final token = ChatConfig.instance.token;
final receiverId = chatService.receiverId;
if (userId == null || token == null || receiverId.isEmpty) {
throw Exception('Not authenticated or no receiver selected');
}
String messageId = DateTime.now().millisecondsSinceEpoch.toString();
// Custom implementation for sending a message
final response = await http.post(
Uri.parse('https://your-custom-api.com/messages'),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
body: jsonEncode({
'messageId': messageId,
'sender': userId,
'receiver': receiverId,
'message': text,
'timestamp': DateTime.now().toIso8601String(),
}),
);
if (response.statusCode == 201) {
// Create and return the sent message
return ChatMessage(
messageId: messageId,
senderId: userId,
receiverId: receiverId,
message: text,
createdAt: DateTime.now(),
status: 'sent',
isMine: true,
);
} else {
throw Exception('Failed to send message: ${response.statusCode}');
}
},
// Custom implementation for deleting messages
deleteMessageHandler: (String messageId) async {
final userId = ChatConfig.instance.userId;
final token = ChatConfig.instance.token;
if (userId == null || token == null) {
throw Exception('Not authenticated');
}
// Custom implementation for deleting a message
final response = await http.delete(
Uri.parse('https://your-custom-api.com/messages/$messageId'),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
);
return response.statusCode == 200;
},
);
// Set the custom API handlers
ChatPlugin.chatService.setApiHandlers(apiHandlers);
}
void setupCustomSocketEvents() {
// Set custom socket event configuration
ChatPlugin.chatService.setSocketEventConfig(
SocketEventConfig(
sendMessageEvent: 'custom_send_message',
receiveMessageEvent: 'custom_receive_message',
joinRoomEvent: 'custom_join_room',
typingStartEvent: 'custom_typing_start',
typingEndEvent: 'custom_typing_end',
messageDeliveredEvent: 'custom_message_delivered',
messagesReadEvent: 'custom_messages_read',
markMessagesReadEvent: 'custom_mark_messages_read',
userStatusChangeEvent: 'custom_user_status_change',
registerUserEvent: 'custom_register_user',
deleteMessageEvent: 'custom_delete_message',
),
);
// Register for custom socket events once socket is connected
Future.delayed(const Duration(seconds: 1), () {
final chatService = ChatPlugin.chatService;
if (chatService.isSocketConnected) {
chatService.registerCustomSocketEvents({
'custom_user_joined': (data) {
print('User joined: $data');
chatService.triggerCustomEvent('userJoined', data);
},
'custom_user_left': (data) {
print('User left: $data');
chatService.triggerCustomEvent('userLeft', data);
},
'custom_message_reaction': (data) {
print('Message reaction: $data');
chatService.triggerCustomEvent('messageReaction', data);
},
});
}
});
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Custom Chat Plugin Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
bool _isLoggedIn = false;
bool _isLoading = false;
final ChatService _chatService = ChatPlugin.chatService;
@override
void initState() {
super.initState();
_checkLoginStatus();
// Listen for custom events
_chatService.addEventListener(
ChatEventType.custom,
'homepage_custom_listener',
(data) {
if (data['eventName'] == 'userJoined') {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('User joined: ${data['data']['username']}'))
);
}
},
);
// Listen for connection status changes
_chatService.addEventListener(
ChatEventType.connectionStatusChanged,
'homepage_connection_listener',
(isConnected) {
print('Connection status changed: $isConnected');
if (!isConnected && _isLoggedIn) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Connection lost. Reconnecting...'))
);
}
},
);
}
Future<void> _checkLoginStatus() async {
setState(() {
_isLoading = true;
});
try {
// Check if userId and token exist in ChatConfig
_isLoggedIn = ChatConfig.instance.userId != null && ChatConfig.instance.token != null;
if (_isLoggedIn) {
// Initialize global connection for notifications
await _chatService.initialize();
}
} catch (e) {
print("Error checking login status: $e");
_isLoggedIn = false;
}
setState(() {
_isLoading = false;
});
}
Future<void> _login() async {
if (_usernameController.text.isEmpty || _passwordController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Please enter username and password')),
);
return;
}
setState(() {
_isLoading = true;
});
try {
// Custom login implementation
final response = await http.post(
Uri.parse('https://your-custom-api.com/login'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'username': _usernameController.text,
'password': _passwordController.text,
}),
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
final userId = data['userId'];
final token = data['token'];
if (userId != null && token != null) {
// Update the chat config with the new credentials
ChatConfig.instance = ChatConfig.instance.updateCredentials(
userId: userId,
token: token,
);
// Update UI
setState(() {
_isLoggedIn = true;
_isLoading = false;
});
// Initialize global connection for notifications
await _chatService.initialize();
return;
}
}
// If we reach here, login failed
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Login failed. Please check your credentials.')),
);
} catch (e) {
print("Login error: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('An error occurred during login')),
);
}
setState(() {
_isLoading = false;
});
}
Future<void> _logout() async {
setState(() {
_isLoading = true;
});
try {
// Disconnect chat service
_chatService.fullDisconnect();
// Clear credentials
ChatConfig.instance = ChatConfig.instance.clearCredentials();
setState(() {
_isLoggedIn = false;
});
} catch (e) {
print("Logout error: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('An error occurred during logout')),
);
}
setState(() {
_isLoading = false;
});
}
// Method to emit a custom socket event
void _emitCustomEvent() {
if (_chatService.isSocketConnected) {
_chatService.emitCustomEvent('custom_test_event', {
'userId': ChatConfig.instance.userId,
'timestamp': DateTime.now().toIso8601String(),
'data': 'Hello from Flutter app!'
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Custom event emitted')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Socket not connected')),
);
}
}
@override
Widget build(BuildContext context) {
if (_isLoading) {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
if (_isLoggedIn) {
return Scaffold(
appBar: AppBar(
title: Text('Custom Chat Demo'),
actions: [
IconButton(
icon: Icon(Icons.send),
onPressed: _emitCustomEvent,
tooltip: 'Emit custom event',
),
IconButton(
icon: Icon(Icons.logout),
onPressed: _logout,
tooltip: 'Logout',
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'You are logged in!',
style: TextStyle(fontSize: 18),
),
SizedBox(height: 20),
Text(
'Connection status: ${_chatService.isSocketConnected ? 'Connected' : 'Disconnected'}',
style: TextStyle(fontSize: 14, color: _chatService.isSocketConnected ? Colors.green : Colors.red),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => MyChatListScreen(),
// ),
// );
},
child: Text('View Conversations'),
),
],
),
),
);
} else {
return Scaffold(
appBar: AppBar(
title: Text('Custom Chat Demo Login'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: _usernameController,
decoration: const InputDecoration(
labelText: 'Username',
border: OutlineInputBorder(),
),
),
SizedBox(height: 10),
TextField(
controller: _passwordController,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _login,
child: Text('Login'),
),
],
),
),
);
}
}
@override
void dispose() {
// Remove event listeners
_chatService.removeEventListener(ChatEventType.custom, 'homepage_custom_listener');
_chatService.removeEventListener(ChatEventType.connectionStatusChanged, 'homepage_connection_listener');
super.dispose();
}
}