← Technology Archive

Historical archive

Building a Reusable WebSocket Service in Flutter

A singleton Flutter WebSocket utility that manages connection state, sends messages, and distributes server events to registered listeners.

This example creates a globally accessible WebSocket utility. A production app can add another business layer on top so connection and message handling remain centralized.

util/socket.dart

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

WebSocketsNotifications sockets = new WebSocketsNotifications();

const String _SERVER_ADDRESS = 'ws://192.168.11.193:12345/ws';

class WebSocketsNotifications {
  static final WebSocketsNotifications _sockets =
      new WebSocketsNotifications._internal();

  factory WebSocketsNotifications() {
    return _sockets;
  }

  WebSocketsNotifications._internal();

  IOWebSocketChannel _channel;
  bool _isOn = false;
  ObserverList<Function> _listeners = new ObserverList<Function>();

  initCommunication() async {
    reset();

    try {
      print('==socket== connect sockets ip: $_SERVER_ADDRESS');
      _channel = new IOWebSocketChannel.connect(_SERVER_ADDRESS);
      _isOn = true;
      _channel.stream.listen(_handleMassageFromServer);
    } catch (e) {
      // Handle connection errors here.
    }
  }

  reset() {
    if (_channel != null) {
      if (_channel.sink != null) {
        print('==socket== close sockets');
        _channel.sink.close();
        _isOn = false;
      }
    }
  }

  send(message) {
    if (_channel != null) {
      if (_channel.sink != null && _isOn) {
        print('==socket== message to server:' + message);
        _channel.sink.add(message);
      }
    }
  }

  addListener(Function callback) {
    _listeners.add(callback);
  }

  removeListener(Function callback) {
    _listeners.remove(callback);
  }

  _handleMassageFromServer(message) {
    print('==socket== message from server:' + message);
    _listeners.forEach((Function callback) {
      callback(message);
    });
  }
}

The singleton owns the channel, tracks whether it is active, and exposes a listener collection for application code. Modern implementations should also handle reconnection, authentication, ping/pong heartbeats, backoff, disposal, and Dart null safety.