Stream Management
Stream management covers publishing your camera into the room, subscribing to remote participants' streams, controlling audio and video, and working with active talkers. All operations use the EnxRtc class; video rendering is handled by the EnxPlayerWidget Flutter widget.
Once you have joined a room, you need to start sending your local camera and microphone feed to the other participants. This is called publishing. The SDK gives you a single method — EnxRtc.publish() — to start that process.
The right moment to call publish() is inside the onRoomConnected callback. This ensures the room is fully established before you attempt to push media into it. Publishing before the room is ready will fail silently or result in an error event.
EnxRtc.onRoomConnected = (Map<dynamic, dynamic> map) {
// Start publishing immediately after connecting
EnxRtc.publish();
};
EnxRtc.onPublishedStream = (Map<dynamic, dynamic> map) {
print('Published. Stream ID: ${map['streamId']}');
setState(() {
localStreamId = map['streamId'].toString();
});
};
The SDK confirms a successful publish through two callbacks:
| Callback | Fires when |
|---|---|
onPublishedStream |
Your local stream has been published. The payload includes streamId, which you must save to render the local preview. |
onStreamAdded |
Fires on all participants in the room — including you — whenever any stream enters the room. Use this to trigger subscription for remote streams. |
Unpublishing Your Stream
If you want to remove your stream from the room without ending the session — for example, when temporarily stepping away — call EnxRtc.unpublish(). Your media is removed from the room but your connection remains active. You can call publish() again at any time to rejoin with your stream.
EnxRtc.unpublish();
EnxRtc.onUnPublishedStream = (Map<dynamic, dynamic> map) {
print('Stream unpublished');
};
EnxPlayerWidget is the Flutter widget responsible for displaying a video stream — whether local or remote. You add one instance of this widget to your widget tree for every stream you want to render. For a typical session with four participants, you would have four EnxPlayerWidget instances: one for your local preview and one for each remote participant.
The widget takes two key parameters:
streamId— the stream ID returned byonPublishedStream(local) oronSubscribedStream(remote).isLocal— set totruefor your own stream,falsefor all remote streams.
// Render the local stream
EnxPlayerWidget(
streamId: localStreamId,
isLocal: true,
)
// Render a remote participant's stream
EnxPlayerWidget(
streamId: remoteStreamId,
isLocal: false,
)
EnxPlayerWidget in a SizedBox or AspectRatio widget to constrain its dimensions. The widget fills its available space by default, which can cause layout issues if placed directly inside an unbounded container such as a Column or ListView.
Most mobile devices have both a front-facing and a rear-facing camera. The Flutter SDK lets you switch between them mid-session without interrupting the call. This is useful when a participant wants to share something in their environment or switch to a more comfortable angle.
Call EnxRtc.switchCamera() at any point while your stream is published. No callback is required — the camera switch takes effect immediately and the updated video feed is automatically sent to all subscribers.
EnxRtc.switchCamera();
During a live session, a participant may connect a Bluetooth headset, plug in a wired headphone, or want to switch from the earpiece to the loudspeaker. The EnxRtc.switchMediaDevice(deviceName) method handles all of these transitions without ending or interrupting the session.
Pass the target device name as a string. The onNotifyDeviceUpdate callback fires when the switch is complete, so you can update your UI to reflect the new active device.
// Switch to speaker
EnxRtc.switchMediaDevice('SPEAKER_PHONE');
// Switch to earpiece
EnxRtc.switchMediaDevice('EARPIECE');
// Listen for device change completion
EnxRtc.onNotifyDeviceUpdate = (String deviceName) {
print('Audio device switched to: $deviceName');
};
The valid device name strings match those returned by EnxRtc.getDevices(): SPEAKER_PHONE, EARPIECE, WIRED_HEADSET, and BLUETOOTH.
Participants frequently need to silence their microphone or stop their camera without leaving the session — for example, when background noise is disruptive, or when they need a moment of privacy. The EnableX Flutter SDK provides dedicated methods for both. When you mute or unmute yourself, the SDK automatically notifies all other participants in the room so their UIs can reflect the change.
Mute Audio — EnxRtc.muteSelfAudio(bool isMute)
Pass true to mute your microphone or false to unmute it. Your audio track is muted locally — the media channel remains open so the switch is near-instant when you unmute.
EnxRtc.muteSelfAudio(true); // Mute microphone
EnxRtc.muteSelfAudio(false); // Unmute microphone
// Acknowledgement fired back to you
EnxRtc.onAudioEvent = (Map<dynamic, dynamic> map) {
print('Audio mute state changed: $map');
};
// Notifications fired to all other participants
EnxRtc.onRemoteStreamAudioMute = (Map<dynamic, dynamic> map) {};
EnxRtc.onRemoteStreamAudioUnMute = (Map<dynamic, dynamic> map) {};
Mute Video — EnxRtc.muteSelfVideo(bool isMute)
Pass true to stop your camera or false to resume it. The video track is paused — remote participants will stop receiving your video frames until you unmute.
EnxRtc.muteSelfVideo(true); // Stop camera
EnxRtc.muteSelfVideo(false); // Resume camera
// Acknowledgement fired back to you
EnxRtc.onVideoEvent = (Map<dynamic, dynamic> map) {
print('Video mute state changed: $map');
};
// Notifications fired to all other participants
EnxRtc.onRemoteStreamVideoMute = (Map<dynamic, dynamic> map) {};
EnxRtc.onRemoteStreamVideoUnMute = (Map<dynamic, dynamic> map) {};
Stream parameters such as bandwidth limits can change over the lifetime of a session — network conditions degrade, the user's device may get throttled, or your application may shift between high-quality and low-quality modes based on the active layout. The EnxRtc.updateConfiguration(config) method lets you adjust these parameters while the session is live, without requiring a reconnect.
Pass a configuration map specifying the bandwidth limits you want to apply. All four fields — maxVideoBW, minVideoBW, maxAudioBW, and minAudioBW — are expressed in kilobits per second (kbps).
Map<String, dynamic> config = {
'maxVideoBW': 150,
'minVideoBW': 100,
'maxAudioBW': 50,
'minAudioBW': 30,
};
EnxRtc.updateConfiguration(config);
When a remote participant publishes their stream, the SDK fires onStreamAdded on all other participants in the room. This is your signal that a new stream is available. To start receiving that participant's audio and video, you call EnxRtc.subscribe(streamId) with the stream ID from the event payload.
After a successful subscription, the SDK fires onSubscribedStream with the confirmed stream ID. At this point, add the ID to your state and render the stream with an EnxPlayerWidget.
EnxRtc.onStreamAdded = (Map<dynamic, dynamic> map) {
String streamId = map['streamId'].toString();
EnxRtc.subscribe(streamId);
};
EnxRtc.onSubscribedStream = (Map<dynamic, dynamic> map) {
String streamId = map['streamId'].toString();
setState(() {
remoteStreamIds.add(streamId);
});
};
Muting All Subscribed Streams — EnxRtc.muteSubscriberStreamsAudio(bool isMute)
This method mutes or unmutes the audio of all remote streams you have subscribed to in a single call. It is useful when you want to silence the entire room — for example, when the local user receives an incoming phone call and needs to step away briefly without ending the session.
EnxRtc.muteSubscriberStreamsAudio(true); // Mute all remote audio
EnxRtc.muteSubscriberStreamsAudio(false); // Unmute all remote audio
In a room with many participants, it is not practical — or visually useful — to display every participant's video simultaneously. EnableX solves this with an active talker system: the server continuously ranks participants by speaking activity and pushes a ranked list of the most active streams to your app in real time. Your app should render this list in a spotlight-style layout, prioritising the participants who are currently speaking.
The Flutter SDK receives up to 6 active talker streams at a time. The list updates automatically whenever the ranking changes — no polling or manual refresh is needed from your app.
EnxRtc.onActiveTalkerList = (Map<dynamic, dynamic> map) {
// map contains the updated active talker list
List<dynamic> talkers = map['ActiveTalkers'] ?? [];
setState(() {
activeTalkers = talkers;
});
};
Query Maximum Talker Count — EnxRtc.getMaxTalkers()
Call this method to find out the absolute maximum number of active talker streams the server is configured to send for this session. The result arrives through the onMaxTalkerCount callback.
EnxRtc.getMaxTalkers();
EnxRtc.onMaxTalkerCount = (Map<dynamic, dynamic> map) {
print('Max talkers: ${map['maxTalkers']}');
};
Query Current Talker Count — EnxRtc.getTalkerCount()
Call this method to query the currently active talker count setting — that is, how many streams your app is currently configured to receive. The result arrives through the onGetTalkerCount callback.
EnxRtc.getTalkerCount();
EnxRtc.onGetTalkerCount = (Map<dynamic, dynamic> map) {
print('Current talker count: ${map['maxTalkers']}');
};
Set Talker Count — EnxRtc.setTalkerCount(int count)
Use this method to control how many active talker streams your app wants to receive at a time. The valid range is 0 to 6. Setting a lower count reduces the number of video panels your layout must render, which can help on lower-powered devices or constrained network connections. The onSetTalkerCount callback confirms the updated setting.
EnxRtc.setTalkerCount(4);
EnxRtc.onSetTalkerCount = (Map<dynamic, dynamic> map) {
print('Talker count updated: $map');
};
count is set to 0, you will still receive 3 audio-only streams — the server does not go completely silent. To receive the maximum 6 video talkers, explicitly set count to 6.