Flutter SDK
TgoRTC Flutter SDK for cross-platform audio/video communication based on LiveKit.
Installation
Add the dependency in pubspec.yaml:
dependencies:
tgortcflutter: ^1.0.0
Dependencies
The SDK automatically includes:
livekit_client: ^2.3.6flutter_webrtc: ^0.12.12
Common Entry Points
TgoRTC.instance.roomManager
TgoRTC.instance.participantManager
TgoRTC.instance.audioManager
Quick Start
1. Initialize SDK
import 'package:tgortcflutter/tgortc.dart';
TgoRTC.instance.init(
Options()
..debug = true
..mirror = false,
);
2. Create Room Info and Join
import 'package:tgortcflutter/entity/const.dart';
import 'package:tgortcflutter/entity/room_info.dart';
final roomInfo = RoomInfo(
'roomName',
'token',
'url',
'loginUID',
'creatorUID',
);
roomInfo.maxParticipants = 9;
roomInfo.rtcType = RTCType.video;
roomInfo.timeout = 30;
await TgoRTC.instance.roomManager.joinRoom(
roomInfo,
micEnabled: true,
cameraEnabled: true,
screenShareEnabled: false,
);
Note:
The SDK currently supports both screenShareEnabled and the legacy typo scrennShareEnabled.
The docs use screenShareEnabled.
3. Listen to Connection Status
import 'package:tgortcflutter/entity/const.dart';
void handleConnectStatus(String roomName, ConnectStatus status, String reason) {
print('Room $roomName: $status - $reason');
switch (status) {
case ConnectStatus.connecting:
print('Connecting...');
break;
case ConnectStatus.connected:
print('Connected to room');
break;
case ConnectStatus.reconnecting:
print('Reconnecting...');
break;
case ConnectStatus.reconnected:
print('Reconnected to room');
break;
case ConnectStatus.disconnected:
print('Disconnected from room');
break;
}
}
TgoRTC.instance.roomManager.addConnectListener(handleConnectStatus);
TgoRTC.instance.roomManager.removeConnectListener(handleConnectStatus);
4. Get Participants
final local = TgoRTC.instance.participantManager.getLocalParticipant();
final remotes = TgoRTC.instance.participantManager.getRemoteParticipants();
final all = TgoRTC.instance.participantManager.getAllParticipants();
TgoRTC.instance.participantManager.addNewParticipantListener((participant) {
print('New participant joined: ${participant.uid}');
});
5. Media Control and Events
Media control is only available for local participants.
import 'package:tgortcflutter/entity/const.dart';
import 'package:tgortcflutter/entity/video_info.dart';
final participant = TgoRTC.instance.participantManager.getLocalParticipant();
await participant?.setMicrophoneEnabled(true);
await participant?.setCameraEnabled(true);
await participant?.setScreenShareEnabled(true);
participant?.switchCamera();
await participant?.setSpeakerphoneOn(true, forceSpeakerOutput: false);
await participant?.toggleSpeakerphone();
final micEnabled = participant?.getMicrophoneEnabled() ?? false;
final cameraEnabled = participant?.getCameraEnabled() ?? false;
final screenShareEnabled = participant?.getScreenShareEnabled() ?? false;
final speakerEnabled = participant?.getSpeakerEnabled() ?? false;
final cameraPosition = participant?.getCameraPosition();
final audioLevel = participant?.getAudioLevel() ?? 0;
final isSpeaking = participant?.getIsSpeaking() ?? false;
final createdAt = participant?.createdAt;
final currentVideoInfo = participant?.currentVideoInfo;
void onMicChange(bool enabled) {
print('Microphone: ${enabled ? "on" : "off"}');
}
void onCameraChange(bool enabled) {
print('Camera: ${enabled ? "on" : "off"}');
}
void onSpeakerChange(bool enabled) {
print('Speaker: ${enabled ? "on" : "off"}');
}
void onScreenShareChange(bool enabled) {
print('Screen share: ${enabled ? "on" : "off"}');
}
void onCameraPositionChange(TgoCameraPosition position) {
print('Camera position: $position');
}
void onSpeakingChange(bool speaking) {
print('Speaking: $speaking');
}
void onConnQualityChange(TgoConnectionQuality quality) {
print('Connection quality: $quality');
}
void onJoined() {
print('Participant joined');
}
void onLeave() {
print('Participant left');
}
void onTrackPublished() {
print('Track published');
}
void onTrackUnpublished() {
print('Track unpublished');
}
void onVideoInfoChange(VideoInfo info) {
print('Video: ${info.resolutionString}, ${info.bitrateString}');
}
participant?.addMicrophoneStatusListener(onMicChange);
participant?.addCameraStatusListener(onCameraChange);
participant?.addSpeakerStatusListener(onSpeakerChange);
participant?.addScreenShareStatusListener(onScreenShareChange);
participant?.addCameraPositionListener(onCameraPositionChange);
participant?.addSpeakingListener(onSpeakingChange);
participant?.addConnQualityListener(onConnQualityChange);
participant?.addJoinedListener(onJoined);
participant?.addLeaveListener(onLeave);
participant?.addTrackPublishedListener(onTrackPublished);
participant?.addTrackUnpublishedListener(onTrackUnpublished);
participant?.addVideoInfoListener(onVideoInfoChange);
participant?.removeMicrophoneStatusListener(onMicChange);
participant?.removeCameraStatusListener(onCameraChange);
participant?.removeSpeakerStatusListener(onSpeakerChange);
participant?.removeScreenShareStatusListener(onScreenShareChange);
participant?.removeCameraPositionListener(onCameraPositionChange);
participant?.removeSpeakingListener(onSpeakingChange);
participant?.removeConnQualityListener(onConnQualityChange);
participant?.removeJoinedListener(onJoined);
participant?.removeLeaveListener(onLeave);
participant?.removeTrackPublishedListener(onTrackPublished);
participant?.removeTrackUnpublishedListener(onTrackUnpublished);
participant?.removeVideoInfoListener(onVideoInfoChange);
6. Render Video Track
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:livekit_client/livekit_client.dart';
import 'package:tgortcflutter/tgortc.dart';
import 'package:tgortcflutter/track/tgo_track_renderer.dart';
class VideoView extends StatefulWidget {
final TgoParticipant participant;
const VideoView({super.key, required this.participant});
State<VideoView> createState() => _VideoViewState();
}
class _VideoViewState extends State<VideoView> {
late final TgoTrackRenderer _renderer;
void initState() {
super.initState();
_renderer = TgoTrackRenderer(
source: TrackSource.camera,
fit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
mirror: true,
placeholder: const SizedBox.shrink(),
onTrackChange: (hasTrack) {
debugPrint('Track available: $hasTrack');
},
);
_renderer.setParticipant(widget.participant);
}
Widget build(BuildContext context) {
return _renderer.build();
}
}
7. Audio Management
final isSpeakerOn = TgoRTC.instance.audioManager.isSpeakerOn;
final canSwitchSpeaker = TgoRTC.instance.audioManager.canSwitchSpeakerphone;
final speakerOn = TgoRTC.instance.audioManager.speakerOn;
final inputDevices = await TgoRTC.instance.audioManager.getAudioInputDevices();
final outputDevices = await TgoRTC.instance.audioManager.getAudioOutputDevices();
final videoDevices = await TgoRTC.instance.audioManager.getDevices('videoinput');
await TgoRTC.instance.audioManager.setSpeakerphoneOn(true);
await TgoRTC.instance.audioManager.toggleSpeakerphone();
if (inputDevices.isNotEmpty) {
await TgoRTC.instance.audioManager.switchAudioInputDevice(
inputDevices.first.deviceId,
);
}
if (outputDevices.isNotEmpty) {
await TgoRTC.instance.audioManager.switchAudioOutputDevice(
outputDevices.first.deviceId,
);
}
void onDeviceChange(List<dynamic> devices) {
print('Devices changed: $devices');
}
TgoRTC.instance.audioManager.addDeviceChangeListener(onDeviceChange);
TgoRTC.instance.audioManager.removeDeviceChangeListener(onDeviceChange);
8. Leave Room
await TgoRTC.instance.roomManager.leaveRoom();
API Reference
TgoRTC
| Property | Type | Description |
|---|---|---|
instance | TgoRTC | Singleton instance |
options | Options | SDK configuration |
roomManager | TgoRoomManager | Room management |
participantManager | TgoParticipantManager | Participant management |
audioManager | TgoAudioManager | Audio management |
TgoRoomManager
| Method | Description |
|---|---|
joinRoom(roomInfo, {micEnabled, cameraEnabled, screenShareEnabled}) | Join a room |
leaveRoom() | Leave current room |
addConnectListener(listener) | Add connection status listener |
removeConnectListener(listener) | Remove connection status listener |
addVideoInfoListener(listener) | Add local video info listener |
removeVideoInfoListener(listener) | Remove local video info listener |
| Property | Description |
|---|---|
room | LiveKit Room instance |
currentRoomInfo | Current room info |
currentVideoInfo | Current local video info |
TgoParticipantManager
| Method | Description |
|---|---|
getLocalParticipant() | Get local participant |
getRemoteParticipants() | Get remote participants |
getAllParticipants() | Get all participants |
getPendingParticipantCreatedAt() | Get creation times of pending participants |
removeParticipantByUid(uid) | Remove a pending participant by UID |
removePendingParticipants(roomName, uids) | Remove pending participants |
missed(roomName, uids) | Mark participants as missed |
inviteParticipant(uids, {roomName}) | Invite participants |
invite(roomName, uids) | Invite participants |
addNewParticipantListener(listener) | Listen for new participants |
removeNewParticipantListener(listener) | Remove new participant listener |
TgoParticipant
Media Control Methods
| Method | Description |
|---|---|
setMicrophoneEnabled(enabled) | Enable/disable microphone |
setCameraEnabled(enabled) | Enable/disable camera |
setScreenShareEnabled(enabled) | Enable/disable screen share |
switchCamera() | Switch front/back camera |
setSpeakerphoneOn(on, {forceSpeakerOutput}) | Set speakerphone |
toggleSpeakerphone() | Toggle speakerphone |
Status Methods
| Method | Description |
|---|---|
getMicrophoneEnabled() | Get microphone status |
getCameraEnabled() | Get camera status |
getScreenShareEnabled() | Get screen share status |
getSpeakerEnabled() | Get speaker status |
getCameraPosition() | Get camera position |
getVideoTrack({source}) | Get video track |
getAudioLevel() | Get current audio level |
getIsSpeaking() | Whether the participant is speaking |
Event Listener Methods
| Method | Description |
|---|---|
addMicrophoneStatusListener(listener) | Listen for microphone status changes |
addCameraStatusListener(listener) | Listen for camera status changes |
addSpeakerStatusListener(listener) | Listen for speaker status changes |
addScreenShareStatusListener(listener) | Listen for screen share status changes |
addCameraPositionListener(listener) | Listen for camera position changes |
addSpeakingListener(listener) | Listen for speaking status changes |
addConnQualityListener(listener) | Listen for connection quality changes |
addJoinedListener(listener) | Listen for participant join |
addLeaveListener(listener) | Listen for participant leave |
addTrackPublishedListener(listener) | Listen for track published events |
addTrackUnpublishedListener(listener) | Listen for track unpublished events |
addVideoInfoListener(listener) | Listen for video info changes |
remove...Listener(listener) | Remove the corresponding listener |
Properties
| Property | Description |
|---|---|
uid | Participant ID |
isLocal | Whether this is the local participant |
isJoined | Whether the participant has joined |
createdAt | Creation time |
currentVideoInfo | Current video info |
TgoTrackRenderer
TgoTrackRenderer({
TrackSource source = TrackSource.camera,
RTCVideoViewObjectFit fit = RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
bool? mirror,
Widget? placeholder,
ValueChanged<bool>? onTrackChange,
})
| Method | Description |
|---|---|
setParticipant(participant) | Set participant |
clear() | Clear participant |
build() | Build the video widget |
TgoAudioManager
| Method / Property | Description |
|---|---|
isSpeakerOn | Whether speakerphone is currently enabled |
canSwitchSpeakerphone | Whether speakerphone switching is supported |
speakerOn | Underlying hardware speaker state |
currentAudioInputDeviceId | Current input device ID |
currentAudioOutputDeviceId | Current output device ID |
addDeviceChangeListener(listener) | Add device change listener |
removeDeviceChangeListener(listener) | Remove device change listener |
setSpeakerphoneOn(on, {forceSpeakerOutput}) | Set speakerphone |
toggleSpeakerphone() | Toggle speakerphone |
getAudioInputDevices() | Get audio input devices |
getAudioOutputDevices() | Get audio output devices |
getDevices(kind) | Get devices by type |
switchAudioInputDevice(deviceId, {setAsDefault = true}) | Switch audio input device |
switchAudioOutputDevice(deviceId, {setAsDefault = true}) | Switch audio output device |
Enums
ConnectStatus
enum ConnectStatus {
connecting,
connected,
reconnecting,
reconnected,
disconnected,
}
RTCType
enum RTCType {
audio,
video,
}
TgoConnectionQuality
enum TgoConnectionQuality {
unknown,
excellent,
good,
poor,
lost,
}
TgoCameraPosition
enum TgoCameraPosition {
front,
back,
}
RoomInfo Class
class RoomInfo {
String roomName;
String token;
String url;
String loginUID;
String creatorUID;
int maxParticipants;
RTCType rtcType;
bool isP2P;
List<String> uidList;
int timeout;
String getP2PToUID();
bool isCreator();
}
Configuration Options
class Options {
bool mirror = false;
bool debug = true;
}
Best Practices
Save Participant References
Avoid repeatedly calling getLocalParticipant(). Save the local participant reference after connection and remove listeners in dispose().
Enable Media After Connection
Based on the actual Flutter integration example, on Android it is safer to join the room first and enable local media after connection to avoid flutter_webrtc state-sync issues on some devices:
await TgoRTC.instance.roomManager.joinRoom(
roomInfo,
micEnabled: false,
cameraEnabled: false,
);
final local = TgoRTC.instance.participantManager.getLocalParticipant();
await local?.setMicrophoneEnabled(true);
await local?.setCameraEnabled(true);
Handle Pending Participants
If your business layer already knows which invited users did not answer, you can remove them manually:
TgoRTC.instance.participantManager.removePendingParticipants(
roomInfo.roomName,
['user_b', 'user_c'],
);
Platform Configuration
Android
Add permissions in android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
iOS
Add permission descriptions in ios/Runner/Info.plist:
<key>NSCameraUsageDescription</key>
<string>Camera access is required for video calls</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is required for voice calls</string>