Flutter Video SDK
The EnableX Flutter SDK (enx_flutter_plugin) brings real-time audio and video to Flutter
applications targeting iOS and Android from a single Dart codebase. All SDK operations are performed through
the EnxRtc class using static method calls and callback property assignments — there is no
JSX component or event-listener pattern; callbacks are simply assigned as properties on the class before the
session begins.
- SDK v3.1.5 — Released: February 4, 2026
- Cross-platform: iOS and Android from a single Dart codebase
- Language: Dart (Flutter)
- Requires Flutter 2.0+, iOS 12+, Android API 21+
Flutter SDK v3.1.5 — Released February 4, 2026
Before writing any Flutter code, your backend server must have three things in place. These are server-side responsibilities — none of these credentials or operations should ever be performed inside the Flutter app itself.
1. App Credentials
Log in to the EnableX Portal and create a project to
obtain your app_id and app_key. These credentials authenticate your server when
calling the EnableX Video API. Never embed them in the Flutter app — treat them as
server secrets.
2. A Room
A Room is a virtual session space. Create it via the Video REST API from your server and store the returned
room_id. The room persists until you delete it, so you typically create it once per meeting and
reuse it across participants.
3. A Token per Participant
Each participant who joins the session needs their own short-lived token. Your server generates the token by
calling the Create Token API with the room_id and participant details, then passes it to the
Flutter app. The app uses this token to authenticate when calling EnxRtc.joinRoom().
app_key is required to generate tokens —
if it is embedded in the app, it can be extracted and abused to create unauthorized sessions.
Add the dependency
The EnableX Flutter SDK is distributed as a pub package. Add it to your project's pubspec.yaml
under the dependencies section:
dependencies:
enx_flutter_plugin: ^3.0.3
Then fetch the package by running:
flutter pub get
iOS setup
Flutter apps on iOS require explicit permission declarations for camera and microphone access. Without these,
iOS will crash the app at runtime the first time the SDK attempts to open either device. Open
ios/Runner/Info.plist and add the following entries:
<key>NSCameraUsageDescription</key>
<string>Camera access is required for video calls.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is required for audio calls.</string>
The EnableX Flutter SDK renders video using Flutter's PlatformView mechanism. To prevent a black video
screen on iOS devices, you must also enable embedded views preview in Info.plist:
<key>io.flutter.embedded_views_preview</key>
<true/>
After adding the permission keys, build the iOS target to ensure everything compiles correctly:
flutter build ios --no-codesign
Android setup
No additional native configuration is required for Android beyond what Flutter handles automatically. Camera
and microphone permissions are declared in the SDK's own AndroidManifest.xml and merged into
your app's manifest at build time by the Android Gradle plugin.
Import the SDK package at the top of any Dart file where you need video functionality. A single import gives you access to all SDK classes and widgets:
import 'package:enx_flutter_plugin/enx_flutter_plugin.dart';
This import provides three primary exports you will work with:
-
EnxRtc— The main SDK class. All methods (joinRoom,publish,record, and so on) and all callbacks are static members of this class. You never instantiate it; you call its methods and assign its callback properties directly. -
EnxPlayerWidget— A Flutter widget for rendering a video stream on screen. Embed one per stream you want to display. Pass the stream's identifier to the widget and it handles the platform-specific rendering layer automatically. -
EnxToolbarWidget— A pre-built annotation toolbar widget used when enabling whiteboard annotation. Drop it into your widget tree to give participants annotation controls without building a custom UI.
The Flutter SDK uses a property assignment pattern for event notifications. Rather than
registering listeners against event name strings (as you would with addEventListener in the Web
SDK), or conforming to a delegate protocol (as with the iOS SDK), you assign handler functions directly to
named static properties on the EnxRtc class.
When the SDK needs to notify your app of an event — a room connection established, a remote user joining, a stream becoming available — it calls whichever function you have assigned to the corresponding callback property. If no function has been assigned, the event is silently ignored.
Assign your callbacks in initState() or in a dedicated setup method, always before calling
EnxRtc.joinRoom():
// Assign callbacks before joining the room
EnxRtc.onRoomConnected = (Map<dynamic, dynamic> map) {
print('Connected to room: $map');
};
EnxRtc.onRoomError = (Map<dynamic, dynamic> map) {
print('Error: ${map['msg']}');
};
EnxRtc.onUserConnected = (Map<dynamic, dynamic> map) {
print('User joined: ${map['name']}');
};
EnxRtc.joinRoom(). Callbacks fire as soon as events
occur — if you assign them after joining, you may miss early events like onRoomConnected
or onStreamAdded.
When an SDK method fails, the SDK does not throw a Dart exception. Instead, it delivers an error payload to
the callback associated with the failed operation. For example, if joinRoom() fails, the error
arrives in EnxRtc.onRoomError. For recording failures, the error arrives in the relevant
recording callback.
Every error payload follows this consistent structure:
{
"errorCode": 1001, // numeric error code
"msg": "Error description",
"desc": "Optional extended description"
}
| Field | Type | Description |
|---|---|---|
errorCode |
Number | Numeric code identifying the error. |
msg |
String | Short human-readable error description. |
desc |
String | Optional extended explanation of the error. |
A robust error handler reads both errorCode for programmatic branching and msg /
desc for user-facing or log messages:
EnxRtc.onRoomError = (Map<dynamic, dynamic> map) {
print('Error ${map['errorCode']}: ${map['msg']}');
if (map['desc'] != null) print('Detail: ${map['desc']}');
};
The Flutter SDK documentation is organized into the following topics: