Connecting to a Session

This page walks through the complete flow for joining an EnableX video session from a Flutter app — selecting an audio output device, joining the room via EnxRtc.joinRoom(), handling connection events, and disconnecting cleanly. Follow the steps in order; each depends on the previous one completing successfully.

Connection Flow

Joining an EnableX session from Flutter involves these steps in sequence:

  1. Optionally query audio devices — find out which output devices are available and let the user choose one.
  2. Assign all callback handlers — register your response functions on EnxRtc before connecting so no early events are missed.
  3. Call EnxRtc.joinRoom() — pass the session token, local stream settings, room settings, and advanced options to initiate the connection.
  4. Handle onRoomConnected — confirm the connection succeeded and proceed to publish your local stream.
  5. Publish your local stream — covered in detail in Stream Management.
  6. Call EnxRtc.disconnect() — cleanly end the session when the user leaves.
Step 1 — Select Audio Output Device

Before joining, you can query which audio output devices are currently available on the device and allow the user to select their preferred route. This is especially useful when multiple devices are connected — for example, when a Bluetooth headset is paired alongside the built-in speaker.

The Flutter SDK exposes two methods for this:

Possible device values returned by getDevices():

// Get all available audio output devices
List<dynamic> devices = EnxRtc.getDevices();
print('Available devices: $devices');

// Get the currently active audio output device
String currentDevice = await EnxRtc.getSelectedDevice();
print('Active device: $currentDevice');
Best practice: Call getDevices() before joining and present the result to the user when a Bluetooth headset is detected. This ensures they start the session on their preferred audio route rather than the system default.
Step 2 & 3 — Register Callbacks and Join the Room

Register Callbacks First

The EnableX Flutter SDK communicates all connection events through callbacks assigned directly on the EnxRtc class. Because some callbacks — such as onRoomConnected — fire immediately when the connection is established, you must register all handlers before calling joinRoom().

Register all callbacks before calling joinRoom(). If you assign handlers after the connection is established, early events such as onRoomConnected and onUserConnected will already have fired and your app will not receive them.

EnxRtc.joinRoom()

EnxRtc.joinRoom() is the single call that connects your Flutter app to an EnableX video session. It authenticates with the JWT token, negotiates the WebRTC connection, and prepares the local media stack. The call is async — your app receives the outcome through the onRoomConnected or onRoomError callbacks.

The method accepts four parameters:

Method Signature

static Future<void> joinRoom(
  String token,
  Map<String, dynamic> localInfo,
  Map<String, dynamic> roomInfo,
  List<dynamic> advanceOptions
)

localInfo — Configuring the Local Stream

The localInfo map tells the SDK what media to capture from the device and how to identify this participant in the room.

Field Type Description
audio bool Capture audio from the microphone.
video bool Capture video from the camera.
data bool Enable the data channel (required for chat and custom signalling).
name String Display name shown to other participants.
audioMuted bool Join with the microphone muted. Audio is still captured but not transmitted.
videoMuted bool Join with the camera off. Video is still captured but not transmitted.
Map<String, dynamic> localInfo = {
  'audio': true,
  'video': true,
  'data': true,
  'name': 'Alice',
  'audioMuted': false,
  'videoMuted': false,
};

roomInfo — Reconnection and Audio-Only Settings

The roomInfo map controls how the SDK handles network disruptions and whether video should be active at all.

Field Type Description
allow_reconnect bool Attempt automatic reconnection if the network drops.
number_of_attempts int Maximum number of reconnection attempts before giving up.
timeout_interval int Seconds to wait between reconnection attempts.
audio_only bool Set true to join in audio-only mode — no video is captured or received.
forceTurn bool Force TURN relay for all connections, even when a direct peer connection is possible. Useful in restrictive network environments.
Map<String, dynamic> roomInfo = {
  'allow_reconnect': true,
  'number_of_attempts': 3,
  'timeout_interval': 20,
  'audio_only': false,
  'forceTurn': false,
};

Full Join Example

The example below shows a complete Flutter StatefulWidget that registers all necessary callbacks in initState() and then calls joinRoom(). This is the recommended pattern — it guarantees that no early events are missed.

import 'package:enx_flutter_plugin/enx_flutter_plugin.dart';

class VideoSessionPage extends StatefulWidget {
  final String token;
  const VideoSessionPage({required this.token, super.key});

  @override
  State<VideoSessionPage> createState() => _VideoSessionPageState();
}

class _VideoSessionPageState extends State<VideoSessionPage> {

  @override
  void initState() {
    super.initState();
    _registerCallbacks();
    _joinRoom();
  }

  void _registerCallbacks() {
    EnxRtc.onRoomConnected = (Map<dynamic, dynamic> map) {
      print('Room connected: $map');
      // Proceed to publish local stream here
    };

    EnxRtc.onRoomError = (Map<dynamic, dynamic> map) {
      print('Room error: ${map['msg']}');
    };

    EnxRtc.onUserConnected = (Map<dynamic, dynamic> map) {
      print('User joined: ${map['name']}');
    };

    EnxRtc.onUserDisConnected = (Map<dynamic, dynamic> map) {
      print('User left: ${map['clientId']}');
    };

    EnxRtc.onRoomDisConnected = (Map<dynamic, dynamic> map) {
      print('Disconnected from room');
    };
  }

  Future<void> _joinRoom() async {
    Map<String, dynamic> localInfo = {
      'audio': true,
      'video': true,
      'data': true,
      'name': 'Alice',
      'audioMuted': false,
      'videoMuted': false,
    };

    Map<String, dynamic> roomInfo = {
      'allow_reconnect': true,
      'number_of_attempts': 3,
      'timeout_interval': 20,
      'audio_only': false,
      'forceTurn': false,
    };

    await EnxRtc.joinRoom(widget.token, localInfo, roomInfo, []);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Video Session')),
      body: Container(), // Add EnxPlayerWidget here for video
    );
  }
}

Callbacks Fired on Connection

Once joinRoom() is called, the SDK fires the following callbacks to report the outcome and participant activity:

Callback Fires when
onRoomConnected Connection established successfully; the map contains room metadata.
onRoomError Connection attempt failed; the map contains an error message.
onUserConnected A new participant has joined the room.
onUserDisConnected A participant has left the room.
onPublishedStream The local stream was successfully published to the room.
onUnPublishedStream The local stream was unpublished from the room.
Step 4 — Disconnect

When the user ends the session — by tapping a leave button, navigating away, or closing the app — your app should call EnxRtc.disconnect(). This method stops local media capture, closes the WebRTC peer connection, and signals the server to remove this participant from the room. The SDK confirms the teardown through the onRoomDisConnected callback.

EnxRtc.disconnect()

// Disconnect when the user taps "End Call"
void endSession() {
  EnxRtc.disconnect();
}

Listen for onRoomDisConnected to confirm the teardown is complete, then navigate away from the video screen:

EnxRtc.onRoomDisConnected = (Map<dynamic, dynamic> map) {
  // Session fully ended — navigate back to the previous screen
  Navigator.of(context).pop();
};
Always call EnxRtc.disconnect() before disposing the widget. This ensures the camera and microphone are released and the server cleans up the participant slot. Disposing the widget without disconnecting can leave a ghost participant in the room and keep media hardware locked.
Reconnection Handling

When allow_reconnect: true is set in roomInfo, the SDK automatically attempts to restore the connection if the network drops — without any additional code required from your app. Your job is to listen to the reconnection lifecycle callbacks and update the UI accordingly so the user knows what is happening.

The SDK fires the following callbacks during a reconnection sequence:

Callback Fires when
onConnectionLost The network connection was lost and reconnection is being attempted.
onConnectionInterrupted The connection was interrupted — for example, the device switched from Wi-Fi to mobile data.
onReconnect The SDK is actively attempting to reconnect (fires once per attempt).
onUserReconnectSuccess This endpoint reconnected successfully and the session is restored.

A typical pattern is to show a status message while reconnecting and clear it once the SDK reports success:

EnxRtc.onConnectionLost = (Map<dynamic, dynamic> map) {
  setState(() {
    _status = 'Reconnecting...';
  });
};

EnxRtc.onConnectionInterrupted = (Map<dynamic, dynamic> map) {
  setState(() {
    _status = 'Connection interrupted. Trying to restore...';
  });
};

EnxRtc.onReconnect = (Map<dynamic, dynamic> map) {
  // Fired for each reconnection attempt — useful for showing attempt count
  print('Reconnection attempt in progress');
};

EnxRtc.onUserReconnectSuccess = (Map<dynamic, dynamic> map) {
  setState(() {
    _status = 'Connected';
  });
};
Reconnection exhausted: If the SDK exceeds number_of_attempts without success, onRoomDisConnected fires and the session ends. Handle this the same way as a normal disconnect — navigate the user back to the lobby and prompt them to rejoin.
Bandwidth Alerts

The EnableX server continuously monitors the network quality of all participants. When available bandwidth degrades to the point where it can no longer support the current number of active video streams, the SDK notifies your app through onRoomBandwidthAlert.

This callback is your signal to adapt the experience — for example, reducing the number of visible video panels, switching the session to audio-only mode, or informing the user that their connection quality is low.

onRoomBandwidthAlert

EnxRtc.onRoomBandwidthAlert = (Map<dynamic, dynamic> map) {
  // The map contains details about the current bandwidth condition
  print('Bandwidth alert: $map');

  // Example adaptive response: switch to audio-only mode
  // or reduce the active talker count in your UI
};
Adapt proactively. When a bandwidth alert fires, consider pausing non-essential video subscriptions or prompting the user to switch to audio-only mode. Reducing the number of active streams can restore quality for all remaining participants.