This Flutter plugin provides secure WebSocket communication with automatic MTE encryption for iOS and Android applications. It enables real-time, bidirectional messaging between Flutter apps and SocketX servers with minimal code and maximum security.
- Overview
- Before You Begin
- Installation
- Quick Start
- API Reference
- Advanced Usage
- Troubleshooting
- Contact Eclypses
SocketX Client is a Flutter plugin that simplifies secure WebSocket communication with automatic Message Tailoring Engine (MTE) encryption. When integrated into your Flutter application:
- Secure WebSocket Connections: Establishes WSS connections with automatic MTE encryption/decryption
- Simple Event-Driven API: Listen for connection events, messages, and errors through reactive streams
- Binary & Text Support: Send and receive both text and binary messages seamlessly
- Cross-Platform: Works on both iOS and Android with native performance
- Minimal Configuration: Connect with just a URL - the plugin handles the complexity
This plugin requires a SocketX server instance that supports the MTE protocol for encoding/decoding messages.
For help getting started with Flutter development, view the online documentation, which offers tutorials, samples, guidance on mobile development, and a full API reference.
Ensure you have the following ready before integrating this plugin:
- SocketX Server Access - A running SocketX server instance with WebSocket endpoint URL
- Flutter SDK - Latest stable version installed (`flutter --version` to check)
- Xcode - Latest version for iOS development (macOS only)
- Android Studio - For Android development with SDK tools installed (Android SDK 26+ required)
💡 Tip: Run `flutter doctor` to verify your development environment is properly configured.
Add the plugin to your `pubspec.yaml` file:
⚠️ Important: YAML indentation is critical! Use exactly 2 or 4 spaces (be consistent), never tabs.
dependencies:
flutter:
sdk: fluttersocketx_client:
git:
url: https://github.com/Eclypses/socketx-client-plugin-flutter.gitref: v2.0.0
⚠️ Requirements:
- iOS: Requires iOS 16.0 or greater
- Android: Requires minSdk 26 (Android 8.0) or greater
Run this command in your project root:
flutter pub getThis downloads the SocketX Client Plugin and its dependencies.
import'package:socketx_client/socketx_client.dart';
import'dart:typed_data'; // For binary message handlingfinal _socketXClient =SocketXClient();Subscribe to the event streams to handle WebSocket events:
@overridevoidinitState() {
super.initState();
// Listen for connection established
_socketXClient.onConnected.listen((_) {
print('Connected to SocketX server!');
setState(() => _isConnected =true);
});
// Listen for text messages
_socketXClient.onMessage.listen((message) {
print('Received text: $message');
});
// Listen for binary messages
_socketXClient.onBinaryMessage.listen((data) {
print('Received binary: ${data.length} bytes');
});
// Listen for errors
_socketXClient.onError.listen((error) {
print('Error: ${error.message} (${error.type})');
setState(() => _isConnected =false);
});
}Future<void> connectToServer() async {
try {
await _socketXClient.connect(
url:'wss://your-socketx-server.com/room',
headers: {
'Authorization':'Bearer your-token', // Optional
},
);
} catch (e) {
print('Connection failed: $e');
}
}// Send text message
_socketXClient.send(text:'Hello, server!');
// Send binary message
_socketXClient.send(binary:Uint8List.fromList([0x01, 0x02, 0x03]));@overridevoiddispose() {
_socketXClient.disconnect();
super.dispose();
}import'package:flutter/material.dart';
import'package:socketx_client/socketx_client.dart';
import'dart:typed_data';
classChatPageextendsStatefulWidget {
@overrideState<ChatPage> createState() =>_ChatPageState();
}
class_ChatPageStateextendsState<ChatPage> {
final _socketXClient =SocketXClient();
final _messageController =TextEditingController();
finalList<String> _messages = [];
bool _isConnected =false;
@overridevoidinitState() {
super.initState();
_setupListeners();
_connect();
}
void_setupListeners() {
_socketXClient.onConnected.listen((_) {
setState(() => _isConnected =true);
});
_socketXClient.onMessage.listen((message) {
setState(() => _messages.add('Received: $message'));
});
_socketXClient.onError.listen((error) {
setState(() {
_isConnected =false;
_messages.add('Error: ${error.message}');
});
});
}
Future<void> _connect() async {
await _socketXClient.connect(
url:'wss://dev-socketx-server.eclypses.com',
);
}
void_sendMessage() {
if (_messageController.text.isNotEmpty) {
_socketXClient.send(text: _messageController.text);
setState(() => _messages.add('Sent: ${_messageController.text}'));
_messageController.clear();
}
}
@overridevoiddispose() {
_socketXClient.disconnect();
_messageController.dispose();
super.dispose();
}
@overrideWidgetbuild(BuildContext context) {
returnScaffold(
appBar:AppBar(
title:Text('SocketX Chat'),
backgroundColor: _isConnected ?Colors.green :Colors.red,
),
body:Column(
children: [
Expanded(
child:ListView.builder(
itemCount: _messages.length,
itemBuilder: (context, index) =>ListTile(
title:Text(_messages[index]),
),
),
),
Padding(
padding:EdgeInsets.all(8.0),
child:Row(
children: [
Expanded(
child:TextField(
controller: _messageController,
decoration:InputDecoration(
hintText:'Type a message...',
border:OutlineInputBorder(),
),
),
),
SizedBox(width:8),
ElevatedButton(
onPressed: _isConnected ? _sendMessage :null,
child:Text('Send'),
),
],
),
),
],
),
);
}
}The main class for WebSocket communication with MTE encryption.
Establishes a WebSocket connection to the specified URL.
Future<void> connect({
requiredString url,
Map<String, String>? headers,
})Parameters:
- `url` (required): WebSocket URL (e.g., 'wss://example.com/room')
- `headers` (optional): HTTP headers to include in the connection request
Example:
await _socketXClient.connect(
url:'wss://dev-socketx-server.eclypses.com/chat',
headers: {'Authorization':'Bearer token123'},
);Closes the WebSocket connection.
voiddisconnect()Example:
_socketXClient.disconnect();Sends a message through the WebSocket. Provide either `text` or `binary`, not both.
voidsend({String? text, Uint8List? binary})Parameters:
- `text` (optional): Text message to send
- `binary` (optional): Binary data to send as Uint8List
Examples:
// Send text
_socketXClient.send(text:'Hello, world!');
// Send binary
_socketXClient.send(binary:Uint8List.fromList([0xFF, 0xAA, 0x55]));All events are exposed as broadcast streams for reactive programming.
Emitted when the WebSocket connection is successfully established.
Stream<void> get onConnectedExample:
_socketXClient.onConnected.listen((_) {
print('Connected to server');
});Emitted when a text message is received from the server.
Stream<String> get onMessageExample:
_socketXClient.onMessage.listen((message) {
print('Received: $message');
});Emitted when binary data is received from the server.
Stream<Uint8List> get onBinaryMessageExample:
_socketXClient.onBinaryMessage.listen((data) {
print('Received ${data.length} bytes');
});Emitted when an error occurs during connection or communication.
Stream<SocketXError> get onErrorExample:
_socketXClient.onError.listen((error) {
print('Error: ${error.message}');
print('Type: ${error.type}');
});Represents an error that occurred during WebSocket operations.
Properties:
- `message` (String): Human-readable error description
- `type` (SocketXErrorType): Category of the error
Error Types:
- `SocketXErrorType.connection`: Connection-related errors
- `SocketXErrorType.encoding`: MTE encoding errors
- `SocketXErrorType.decoding`: MTE decoding errors
- `SocketXErrorType.unknown`: Other errors
Connect to different SocketX server rooms by changing the URL:
String selectedRoom ='chat';
String baseUrl ='wss://dev-socketx-server.eclypses.com';
Future<void> switchRoom(String room) async {
// Disconnect from current room
_socketXClient.disconnect();
// Connect to new roomString url = room.isEmpty ? baseUrl :'$baseUrl/$room';
await _socketXClient.connect(url: url);
}For applications that use binary protocols (e.g., Protocol Buffers, MessagePack):
import'dart:convert';
// Send JSON as binaryvoidsendJsonAsBinary(Map<String, dynamic> data) {
final jsonString = json.encode(data);
final bytes = utf8.encode(jsonString);
_socketXClient.send(binary:Uint8List.fromList(bytes));
}
// Receive and decode binary JSON
_socketXClient.onBinaryMessage.listen((data) {
final jsonString = utf8.decode(data);
final decoded = json.decode(jsonString);
print('Received data: $decoded');
});Track connection state for UI updates:
enumConnectionState { disconnected, connecting, connected }
classSocketManager {
final _socketXClient =SocketXClient();
ConnectionState _state =ConnectionState.disconnected;
ConnectionStateget state => _state;
Future<void> connect(String url) async {
_state =ConnectionState.connecting;
_socketXClient.onConnected.listen((_) {
_state =ConnectionState.connected;
});
_socketXClient.onError.listen((error) {
_state =ConnectionState.disconnected;
});
await _socketXClient.connect(url: url);
}
}Implement automatic reconnection on connection loss:
classAutoReconnectSocket {
final _socketXClient =SocketXClient();
String? _lastUrl;
bool _shouldReconnect =true;
int _reconnectAttempts =0;
staticconst _maxReconnectAttempts =5;
Future<void> connect(String url) async {
_lastUrl = url;
await _socketXClient.connect(url: url);
_reconnectAttempts =0;
}
void_setupAutoReconnect() {
_socketXClient.onError.listen((error) async {
if (!_shouldReconnect || _lastUrl ==null) return;
if (_reconnectAttempts < _maxReconnectAttempts) {
_reconnectAttempts++;
final delay =Duration(seconds:pow(2, _reconnectAttempts).toInt());
print('Reconnecting in ${delay.inSeconds}s (attempt $_reconnectAttempts)');
awaitFuture.delayed(delay);
try {
await _socketXClient.connect(url: _lastUrl!);
_reconnectAttempts =0;
} catch (e) {
print('Reconnection failed: $e');
}
}
});
}
voiddisconnect() {
_shouldReconnect =false;
_socketXClient.disconnect();
}
}| Issue | Cause | Solution |
|---|---|---|
| Connection fails immediately | Invalid WebSocket URL | Verify URL starts with `wss://` or `ws://` |
| `onError` fires with encoding error | MTE initialization failed | Check SocketX server is running and accessible |
| Messages not received | Not subscribed to streams | Ensure you've set up `.listen()` on event streams before connecting |
| App crashes on Android | minSdk too low | Set `minSdk = 26` in `android/app/build.gradle.kts` |
| Build fails on iOS | Deployment target too low | Set iOS deployment target to 16.0+ in Xcode |
| Connection drops randomly | Network instability | Implement auto-reconnection (see Advanced Usage) |
- Enable verbose logging: Check your SocketX server logs to see connection attempts and errors
- Test with demo server: Use `wss://dev-socketx-server.eclypses.com` to verify your code works
- Check network permissions: Ensure your app has internet permission:
- Android: Check `AndroidManifest.xml` has ``
- iOS: Check `Info.plist` allows arbitrary loads for development servers
- Monitor event streams: Add listeners to all streams during development to see what's happening
voidsetupDebugLogging() {
_socketXClient.onConnected.listen((_) {
print('[SocketX] ✅ Connected');
});
_socketXClient.onMessage.listen((msg) {
print('[SocketX] 📩 Message: $msg');
});
_socketXClient.onBinaryMessage.listen((data) {
print('[SocketX] 📦 Binary: ${data.length} bytes');
});
_socketXClient.onError.listen((error) {
print('[SocketX] ❌ Error (${error.type}): ${error.message}');
});
}- Example Project: Check the `example/` folder in this repository for a complete working implementation
- Issues: Report bugs on GitHub Issues
- Contact: Reach out to Eclypses support (see contact section below)
Email: info@eclypses.com
Web: www.eclypses.com
All trademarks of Eclypses Inc. may not be used without Eclypses Inc.'s prior written consent. No license for any use thereof has been granted without express written consent. Any unauthorized use thereof may violate copyright laws, trademark laws, privacy and publicity laws and communications regulations and statutes. The names, images and likeness of the Eclypses logo, along with all representations thereof, are valuable intellectual property assets of Eclypses, Inc. Accordingly, no party or parties, without the prior written consent of Eclypses, Inc., (which may be withheld in Eclypses' sole discretion), use or permit the use of any of the Eclypses trademarked names or logos of Eclypses, Inc. for any purpose other than as part of the address for the Premises, or use or permit the use of, for any purpose whatsoever, any image or rendering of, or any design based on, the exterior appearance or profile of the Eclypses trademarks and or logo(s).
