Skip to main content

Using WebSockets and Real-Time Communication

1. Understanding WebSocket Protocol

Aspect Description Details
Connection Type Full-duplex communication Allows simultaneous bidirectional data flow between client and server
Handshake HTTP Upgrade Request Initial HTTP request with headers like Sec-WebSocket-Key and Sec-WebSocket-Version
Protocol WebSocket Subprotocol Can define custom subprotocols for specific use cases (e.g., graphql-ws)
Message Types Text and Binary Supports both UTF-8 text messages and binary data (e.g., images, audio)
State Management Open, Closing, Closed WebSocket lifecycle includes OPEN, CLOSING, and CLOSED states

Example: Basic WebSocket Connection Setup

import 'dart:html';

WebSocket webSocket = WebSocket('wss://example.com/websocket');
webSocket.onOpen.listen((event) {
  print('WebSocket connected');
});

webSocket.onMessage.listen((event) {
  print('Received: ${event.data}');
});

webSocket.onClose.listen((event) {
  print('WebSocket closed');
});
Note: WebSocket connections are persistent and must be explicitly closed to free resources.

WebSocket Protocol Advantages

  • Low latency communication
  • Reduced overhead compared to HTTP polling
  • Supports real-time updates and live data streams
  • Used in chat apps, live dashboards, and collaborative tools

2. Connecting to WebSocket Server

Property Description Example
WebSocket Class for creating WebSocket connections WebSocket ws = WebSocket('wss://example.com/ws');
url WebSocket server URL WebSocket('wss://api.example.com/websocket');
protocols Optional list of sub-protocols WebSocket('ws://localhost:8080', protocols: ['v1', 'v2']);

Example: Basic WebSocket Connection

import 'dart:html';

WebSocket ws = WebSocket('wss://echo.websocket.org');

ws.onOpen.listen((event) {
  print('Connected to WebSocket');
});

ws.onMessage.listen((event) {
  print('Received: ${event.data}');
});
Note: Use wss:// for secure connections and ws:// for non-secure.
Warning: Ensure the WebSocket server supports the ws:// or wss:// protocol.

Connection Steps

  • Create a WebSocket instance with the server URL
  • Listen to onOpen for successful connection
  • Handle messages via onMessage
  • Manage connection lifecycle with close()

3. Sending WebSocket Messages

Method Description Parameters Notes
WebSocket.send() Sends a message over the WebSocket connection message: String or Uint8List Must be called after the connection is open. Throws if not connected.
WebSocket.add() Alias for WebSocket.send() message: String or Uint8List Same behavior as send()

Example: Sending a Text Message

final socket = WebSocket('wss://example.com/websocket');
socket.add('Hello, server!');

Example: Sending Binary Data

final data = Uint8List.fromList([1, 2, 3, 4]);
socket.add(data);
Note: Ensure the WebSocket is in the open state before sending messages. Use socket.readyState to check connection status.

4. Receiving WebSocket Messages

Method Description Parameters Notes
onMessage Callback for handling incoming WebSocket messages WebSocketChannel channel, dynamic message Triggered when a message is received from the server
onError Callback for handling WebSocket errors WebSocketChannel channel, dynamic error Invoked when an error occurs during message reception
onDone Callback when the WebSocket connection closes WebSocketChannel channel Executed once the connection is closed gracefully

Example: Receiving WebSocket Messages

import 'package:web_socket_channel/web_socket_channel.dart';

void main() {
  final channel = WebSocketChannel.connect(
    Uri.parse('ws://localhost:8080/websocket'),
  );

  channel.stream.listen(
    (message) {
      print('Received: $message');
    },
    onError: (error) {
      print('WebSocket error: $error');
    },
    onDone: () {
      print('WebSocket connection closed');
    },
  );
}
Note: Ensure that the WebSocket server is running and accessible before attempting to receive messages.

Key Points

  • onMessage is the primary handler for incoming messages.
  • onError should be used to handle any connection or parsing errors.
  • onDone is useful for cleanup or notifying the UI of a closed connection.

5. Handling WebSocket Errors

Error Type Description Handling Strategy
WebSocketException Thrown when a WebSocket connection fails or encounters an error during communication. Use try-catch blocks to handle exceptions and provide fallback behavior or retry logic.
SocketException Occurs when the socket connection is lost or cannot be established due to network issues. Implement reconnection logic with exponential backoff and notify the user of connection status changes.
FormatException Occurs when the received data cannot be parsed correctly (e.g., invalid JSON). Validate incoming messages and handle parsing errors gracefully, logging the malformed data for debugging.

Example: Handling WebSocket Errors in Flutter

import 'dart:io';
import 'package:web_socket_channel/web_socket_channel.dart';

void handleWebSocketErrors() {
  final channel = WebSocketChannel.connect(
    Uri.parse('ws://example.com/websocket'),
  );

  channel.stream.listen(
    (message) {
      try {
        // Attempt to parse the message
        final data = jsonDecode(message);
        print('Received: $data');
      } catch (e) {
        print('Failed to parse message: $message');
      }
    },
    onError: (error) {
      if (error is SocketException) {
        print('Network error occurred');
      } else if (error is WebSocketException) {
        print('WebSocket error: $error');
      } else {
        print('Unexpected error: $error');
      }
    },
    onDone: () {
      print('WebSocket connection closed');
    },
  );
}
Note: Always implement proper error handling for WebSocket connections to ensure robustness in real-time applications.

6. Closing WebSocket Connections

Method Description Notes
WebSocket.close() Initiates the closing handshake for a WebSocket connection Gracefully closes the connection, sending a close frame to the server
WebSocket.close([code], [reason]) Close with optional status code and reason Code must be an integer between 1000-4999. Reason is a string explaining the closure

Example: Closing a WebSocket Connection

final socket = WebSocket("wss://example.com/socket");
// ... perform operations ...
socket.close(1000, "Client initiated close");
Note: The close() method is asynchronous and does not immediately terminate the connection. The actual closure happens after the close frame is sent and acknowledged.

7. Implementing Reconnection Logic

Example: Basic WebSocket Reconnection

import 'dart:io';
import 'package:web_socket_channel/web_socket_channel.dart';

class WebSocketManager {
  WebSocketChannel? channel;
  String url = 'ws://example.com/websocket';
  int maxRetries = 5;
  int retryCount = 0;

  Future<void> connect() async {
    try {
      channel = WebSocketChannel.connect(Uri.parse(url));
      await channel!.stream.first;
      retryCount = 0; // Reset on successful connection
    } catch (e) {
      if (retryCount < maxRetries) {
        retryCount++;
        await Future.delayed(Duration(seconds: 2 * retryCount));
        await connect(); // Retry
      }
    }
  }

  void disconnect() {
    channel?.sink.close();
  }
}
Property Description Default Value
maxRetries Maximum number of reconnection attempts 5
retryCount Current reconnection attempt count 0
url WebSocket server URL to connect to ws://example.com/websocket
Note: Reconnection logic should handle network interruptions gracefully and avoid excessive retries that may overwhelm the server.

Reconnection Workflow

  1. Attempt to establish WebSocket connection
  2. If failed, increment retry count
  3. Wait with exponential backoff before retrying
  4. Repeat until max retries reached or connection established

8. Using Socket.IO Package

The socket_io_client package is the most popular choice for integrating Socket.IO functionality into Flutter applications. It provides a Dart implementation that closely mirrors the JavaScript Socket.IO client.

Feature Description Notes
socket.connect() Establishes a connection to the Socket.IO server Must be called before emitting or listening to events
socket.emit() Sends a message to the server Can include data payload as second parameter
socket.on() Registers a listener for an event Callback receives data payload from server
socket.disconnect() Closes the connection to the server Recommended to call when no longer needed

Example: Initializing Socket.IO Connection

import 'package:socket_io_client/socket_io_client.dart' as io;

void main() {
  io.Socket socket = io.io('http://localhost:3000');
  socket.connect();
}

Example: Listening to Events

socket.on('message', (data) {
  print('Received message: $data');
});

Example: Sending Events

socket.emit('new_message', {
  'user': 'John',
  'text': 'Hello everyone!'
});
Note: Ensure that your server is configured to accept connections from the Flutter app's origin, especially when running in a browser or mobile environment.

9. Implementing Real-Time Chat Features

Example: Basic WebSocket Connection Setup

import 'dart:io';
import 'package:web_socket_channel/web_socket_channel.dart';

void main() async {
  final channel = WebSocketChannel.connect(
    Uri.parse('ws://localhost:8080/chat'),
  );

  channel.stream.listen(
    (message) {
      print('Received: $message');
    },
    onError: (error) {
      print('WebSocket error: $error');
    },
    onDone: () {
      print('WebSocket closed');
    },
  );
}
Property Description Values
WebSocketChannel.connect() Creates a WebSocket connection to the specified URI Uri
stream.listen() Subscribes to messages from the WebSocket onData, onError, onDone

Example: Sending Messages to Server

void sendMessage(WebSocketChannel channel, String message) {
  channel.sink.add(message);
}

// Usage
sendMessage(channel, 'Hello, Server!');
Note: Ensure the server supports WebSocket connections and is configured to handle real-time chat messages.

Key Steps for Real-Time Chat

  • Establish a WebSocket connection to the chat server
  • Listen for incoming messages using stream.listen()
  • Send outgoing messages via sink.add()
  • Handle connection errors and closure gracefully

10. Managing WebSocket State

Property Description Values Notes
WebSocketState Represents the current state of a WebSocket connection WebSocketState.connecting, WebSocketState.open, WebSocketState.closing, WebSocketState.closed Used to check the connection status in real-time

Example: Checking WebSocket State

import 'dart:io';

final socket = await WebSocket.connect('wss://example.com/socket');

print(socket.readyState); // WebSocketState.open

// Listen to state changes
socket.listen(
  (data) {
    print("Received: $data");
  },
  onDone: () {
    print("WebSocket closed");
  },
);

// Check state before sending data
if (socket.readyState == WebSocketState.open) {
  socket.add("Hello, server!");
}
Note: The readyState property is read-only and reflects the current state of the WebSocket connection.

WebSocket State Management Best Practices

  • Always check readyState before sending data to avoid errors
  • Handle WebSocketState.closing and WebSocketState.closed to clean up resources
  • Use socket.listen() to monitor state transitions and update UI accordingly

Was this article helpful?