WebSocket Reconnection in Flutter: Keep Your Real-Time App Alive

WebSocket Reconnection in Flutter: Keep Your Real-Time App Alive

TB

Teqani Blogs

Writer at Teqani

May 21, 20254 min read

Real-time applications such as live chats and financial dashboards require continuous WebSocket connectivity. This article guides you through implementing automatic WebSocket reconnection in Flutter, ensuring a smooth user experience.

Setting Up the Flutter Project

First, add the necessary dependencies to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  web_socket_channel: ^2.3.0
  rxdart: ^0.27.4

Creating a Reconnecting WebSocket Client

Encapsulate WebSocket logic into a reusable class:

import 'dart:async';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:rxdart/rxdart.dart';

class ReconnectingWebSocket {
  final String url;
  WebSocketChannel? _channel;
  Timer? _reconnectTimer;
  bool _manuallyClosed = false;
  int _retrySeconds = 1;
  final BehaviorSubject<String> _messageStream = BehaviorSubject<String>();
  Stream<String> get messages => _messageStream.stream;

  ReconnectingWebSocket(this.url);

  void connect() {
    _manuallyClosed = false;
    _channel = WebSocketChannel.connect(Uri.parse(url));
    _channel!.stream.listen(
      (message) {
        _retrySeconds = 1; // reset on success
        _messageStream.add(message);
      },
      onError: (error) {
        print("Error: $error");
        _scheduleReconnect();
      },
      onDone: () {
        print("Connection closed");
        _scheduleReconnect();
      },
    );
    print('Connecting to $url...');
  }

  void _scheduleReconnect() {
    if (_manuallyClosed) return;
    _reconnectTimer?.cancel();
    _reconnectTimer = Timer(Duration(seconds: _retrySeconds), () {
      connect();
      _retrySeconds = (_retrySeconds * 2).clamp(1, 64); // exponential backoff
    });
    print('Scheduled reconnect in $_retrySeconds seconds');
  }

  void send(String message) {
    try {
      _channel?.sink.add(message);
    } catch (e) {
      print("Send failed: $e");
    }
  }

  void disconnect() {
    _manuallyClosed = true;
    _reconnectTimer?.cancel();
    _channel?.sink.close();
    _messageStream.close();
    print("Disconnected manually");
  }
}

Flutter UI for Testing

Create a simple UI to test the reconnection logic:

import 'package:flutter/material.dart';
import 'reconnecting_websocket.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final ReconnectingWebSocket socket = ReconnectingWebSocket('wss://echo.websocket.events');

  @override
  Widget build(BuildContext context) {
    socket.connect();
    return MaterialApp(
      home: WebSocketTestPage(socket: socket),
    );
  }
}

class WebSocketTestPage extends StatefulWidget {
  final ReconnectingWebSocket socket;
  WebSocketTestPage({required this.socket});
  @override
  _WebSocketTestPageState createState() => _WebSocketTestPageState();
}

class _WebSocketTestPageState extends State<WebSocketTestPage> {
  final TextEditingController _controller = TextEditingController();
  void _send() {
    if (_controller.text.isNotEmpty) {
      widget.socket.send(_controller.text);
      _controller.clear();
    }
  }

  @override
  void dispose() {
    widget.socket.disconnect();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('WebSocket Reconnect Demo')),
      body: Column(
        children: [
          Expanded(
            child: StreamBuilder<String>(
              stream: widget.socket.messages,
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return Center(child: Text('Message: ${snapshot.data}'));
                }
                return Center(child: Text('Waiting for messages...'));
              },
            ),
          ),
          Padding(
            padding: EdgeInsets.all(12),
            child: Row(
              children: [
                Expanded(child: TextField(controller: _controller)),
                IconButton(icon: Icon(Icons.send), onPressed: _send),
              ],
            ),
          )
        ],
      ),
    );
  }
}

Testing and Production Tips

  • Use SSL (wss://) for production.
  • Integrate auth tokens before connection.
  • Add ping/pong heartbeats for connection health.
  • Create a connection status stream for better UX.

By implementing the ReconnectingWebSocket client, you've enabled auto-reconnect with exponential backoff, managed streams with rxdart, and achieved a clean separation of UI and connection logic. You can now build more robust, resilient Flutter apps.

TB

Teqani Blogs

Verified
Writer at Teqani

Senior Software Engineer with 10 years of experience

May 21, 2025
Teqani Certified

All blogs are certified by our company and reviewed by our specialists
Issue Number: #beaaf720-278f-480a-81df-9736d7a52b0c