Before using Stringee Video Conference API for the first time, you must have a Stringee account If you do not have a Stringee account, sign up for free here: https://developer.stringee.com/account/register
Create a Project on Stringee Dashboard

Install stringee-plugin from pub.dev by running the following command from the project root:
$ flutter pub add stringee_plugin:^1.3.2
Check out plugin's documentation for more information.
Stringee Plugin 1.3.2 requires JDK 17 and Android API 21 or later. Set compileSdk to 36 and minSdk to 21 or later in android/app/build.gradle.
android/app/src/main/AndroidManifest.xml<!-- Internet access -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- Audio and camera access -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- Bluetooth headset support -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
Request RECORD_AUDIO, CAMERA, and, on Android 12 or later, BLUETOOTH_CONNECT at runtime before using the corresponding feature.
Stringee Plugin 1.3.2 requires iOS 13.0 or later. Set platform :ios, '13.0' in your ios/Podfile.
From the command line run following command:
pod install --repo-update
After running CocoaPods, open the generated .xcworkspace file.
Add the following keys to ios/Runner/Info.plist before the final </dict> element:
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) uses Camera</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) uses Microphone</string>
To connect to Stringee Server, 3-party authentication is required as described here: Client authentication
For testing, go to Dashboard -> Tools -> Generate Access token and generate an access_token. In production, your server should generate the access_token. See the access token examples.
import 'dart:async';
import 'package:stringee_plugin/stringee_plugin.dart';
...
final StringeeClient _client = StringeeClient();
StreamSubscription? _clientSubscription;
StreamSubscription? _roomSubscription;Register the client's events in your State
class _MyHomePageState extends State<MyHomePage> {
...
@override
void initState() {
super.initState();
...
/// Listen for the StringeeClient event
_clientSubscription = _client.eventStreamController.stream.listen((event) {
Map<dynamic, dynamic> map = event;
switch (map['eventType']) {
case StringeeClientEvents.didConnect:
handleDidConnectEvent();
break;
case StringeeClientEvents.didDisconnect:
handleDiddisconnectEvent();
break;
case StringeeClientEvents.didFailWithError:
int code = map['body']['code'];
String msg = map['body']['message'];
handleDidFailWithErrorEvent(code,msg);
break;
case StringeeClientEvents.requestAccessToken:
handleRequestAccessTokenEvent();
break;
case StringeeClientEvents.didReceiveCustomMessage:
handleDidReceiveCustomMessageEvent(map['body']);
break;
default:
break;
}
});
...
}
...
/// Invoked when the StringeeClient is connected
void handleDidConnectEvent() {}
/// Invoked when the StringeeClient is disconnected
void handleDiddisconnectEvent() {}
/// Invoked when the StringeeClient connection fails
void handleDidFailWithErrorEvent(int code, String message) {}
/// Invoked when your token is expired
void handleRequestAccessTokenEvent() {}
/// Invoked when get Custom message
void handleDidReceiveCustomMessageEvent(Map<dynamic, dynamic> map) {}
...
}
@override
void initState() {
super.initState();
...
String token = 'PUT YOUR TOKEN HERE';
_client.connect(token);
...
}After the client connects to Stringee server, follow these steps to connect to a room:
Initialize StringeeVideo
import 'package:stringee_plugin/stringee_plugin.dart';
...
late StringeeVideo _video;
...
_video = StringeeVideo(_client);
When creating StringeeVideo, you must pass StringeeClient, which you used to connect in step 4.
Create and join room
To create a room, use the Room Management REST API.
To join the room, you need a room credential named room_token. In production, the room token should be generated by your server as described in Room token.
StringeeVideoRoom? _room;
StringeeVideoTrack? _localTrack;
StringeeVideoTrackInfo? _remoteTrackInfo;
StringeeVideoTrack? _remoteTrack;
...
_video.joinRoom('YOUR_ROOM_TOKEN').then((value) {
if (value['status']) {
_room = value['body']['room'];
registerRoomEvents(_room!);
List<StringeeVideoTrackInfo> trackInfos = value['body']['videoTrackInfos'];
List<StringeeRoomUser> users = value['body']['users'];
}
});
In which:
Register the room's events
/// Register events only after joinRoom returns a StringeeVideoRoom object.
void registerRoomEvents(StringeeVideoRoom room) {
_roomSubscription = room.eventStreamController.stream.listen((event) {
Map<dynamic, dynamic> map = event;
switch (map['eventType']) {
case StringeeRoomEvents.didJoinRoom:
handleJoinRoomEvent(map['body']);
break;
case StringeeRoomEvents.didLeaveRoom:
handleLeaveRoomEvent(map['body']);
break;
case StringeeRoomEvents.didAddVideoTrack:
handleAddVideoTrackEvent(map['body']);
break;
case StringeeRoomEvents.didRemoveVideoTrack:
handleRemoveVideoTrackEvent(map['body']);
break;
case StringeeRoomEvents.didReceiveRoomMessage:
handleReceiveRoomMessageEvent(map['body']);
break;
case StringeeRoomEvents.trackReadyToPlay:
handleTrackReadyToPlayEvent(map['body']);
break;
default:
break;
}
});
}
...
/// Invoked when the another user join room
void handleJoinRoomEvent(StringeeRoomUser joinUser) {}
/// Invoked when the another user leave room
void handleLeaveRoomEvent(StringeeRoomUser leaveUser) {}
/// Invoked when the add track to room
void handleAddVideoTrackEvent(StringeeVideoTrackInfo addTrackInfo) {
_remoteTrackInfo = addTrackInfo;
}
/// Invoked when the remove track from room
void handleRemoveVideoTrackEvent(StringeeVideoTrackInfo removeTrackInfo) {
_remoteTrackInfo = null;
_remoteTrack = null;
}
/// Invoked when receive message in room
void handleReceiveRoomMessageEvent(Map<dynamic, dynamic> bodyMap) {}
/// Invoked when track is ready to display video
void handleTrackReadyToPlayEvent(StringeeVideoTrack track) {
_remoteTrack = track;
}
/// Create video track options
StringeeVideoTrackOption options = StringeeVideoTrackOption(
audio: true,
video: true,
screen: false,
);
/// Create local video track
_video.createLocalVideoTrack(options).then((value) {
if (value['status']) {
/// Save the local track, then publish it to the room.
_localTrack = value['body'];
_room!.publish(_localTrack!).then((value) {
if (value['status']) {
// Success
}
});
}
});
After receiving another video track information, you need to subscribe the track to display the video
final trackInfo = _remoteTrackInfo;
if (trackInfo == null) {
return;
}
StringeeVideoTrackOption options = StringeeVideoTrackOption(
audio: trackInfo.audioEnable,
video: trackInfo.videoEnable,
screen: trackInfo.isScreenCapture,
);
_room!.subscribe(trackInfo, options).then((value) {
if (value['status']) {
// Success
}
});
After subscribing another video track successfully or publishing your track successfully, you can display the video
final track = _remoteTrack;
if (track == null) {
return const SizedBox.shrink();
}
StringeeVideoView videoView = track.attach(
isMirror: track.isLocal,
height: 200.0,
width: 150.0,
scalingType: ScalingType.fit,
);
...
return new Scaffold(
backgroundColor: Colors.black,
body: new Stack(
children: <Widget>[
...
videoView,
],
),
);
If you want to stop publishing your track in the room, remove it by unpublishing it:
final localTrack = _localTrack;
if (localTrack == null) {
return;
}
_room!.unpublish(localTrack).then((result) {
if (result['status']) {
// Success
}
});
If you don't want to receive another track's audio or video in the room, you can unsubscribe this track:
final trackInfo = _remoteTrackInfo;
if (trackInfo == null) {
return;
}
_room!.unsubscribe(trackInfo).then((value) {
if (value['status']) {
// Success
}
});
To leave the room, call leave(...) on the StringeeVideoRoom object:
_room!.leave(allClient: false).then((result) {
if (result['status']) {
// Success
}
});
Mute the local sound:
bool mute = true; // true: mute, false: unmute
final localTrack = _localTrack;
if (localTrack == null) {
return;
}
localTrack.mute(mute).then((result) {
if (result['status']) {
///success
}
});
Switch the local camera:
final localTrack = _localTrack;
if (localTrack == null) {
return;
}
localTrack.switchCamera().then((result) {
if (result['status']) {
///success
}
});
Turn on/off video:
bool enableVideo = true; // true: turn on, false: turn off
final localTrack = _localTrack;
if (localTrack == null) {
return;
}
localTrack.enableVideo(enableVideo).then((result) {
if (result['status']) {
///success
}
});
You can view a completed version of this sample app on GitHub: Stringee Flutter plugin example