Local Participant
Manage local user media devices, control audio, video, and screenshare, and handle events in RealtimeKit meetings.
The local user is accessible via meeting.self and contains all information and methods related to the current participant. This includes media controls, device management, participant metadata, and state information.
Access participant identifiers and display information:
// Participant identifiersmeeting.self.id; // Peer ID (unique per session)meeting.self.userId; // User ID (persistent across sessions)meeting.self.customParticipantId; // Custom identifier set by developermeeting.self.name; // Display namemeeting.self.picture; // Display picture URLimport { useRealtimeKitSelector } from "@cloudflare/realtimekit-react";
// Participant identifiersconst id = useRealtimeKitSelector((m) => m.self.id);const userId = useRealtimeKitSelector((m) => m.self.userId);const customParticipantId = useRealtimeKitSelector( (m) => m.self.customParticipantId,);const name = useRealtimeKitSelector((m) => m.self.name);const picture = useRealtimeKitSelector((m) => m.self.picture);// Participant identifiersmeeting.localUser.id // Peer ID (unique per session)meeting.localUser.userId // User ID (persistent across sessions)meeting.localUser.customParticipantId // Custom identifier set by developermeeting.localUser.name // Display namemeeting.localUser.picture // Display picture URL// Participant identifiersmeeting.localUser.id // Peer ID (unique per session)meeting.localUser.userId // User ID (persistent across sessions)meeting.localUser.customParticipantId // Custom identifier set by developermeeting.localUser.name // Display namemeeting.localUser.picture // Display picture URLimport { useRealtimeKitSelector } from "@cloudflare/realtimekit-react-native";
// Participant identifiersconst id = useRealtimeKitSelector((m) => m.self.id);const userId = useRealtimeKitSelector((m) => m.self.userId);const customParticipantId = useRealtimeKitSelector( (m) => m.self.customParticipantId,);const name = useRealtimeKitSelector((m) => m.self.name);const picture = useRealtimeKitSelector((m) => m.self.picture);// Participant identifiersmeeting.localUser.id // Peer ID (unique per session)meeting.localUser.userId // User ID (persistent across sessions)meeting.localUser.customParticipantId // Custom identifier set by developermeeting.localUser.name // Display namemeeting.localUser.picture // Display picture URLAccess the local user's media tracks and states:
// Media state flagsmeeting.self.audioEnabled; // Boolean: Is audio enabled?meeting.self.videoEnabled; // Boolean: Is video enabled?meeting.self.screenShareEnabled; // Boolean: Is screen share active?
// Media tracks (MediaStreamTrack objects)meeting.self.audioTrack; // Audio MediaStreamTrack (available when audioEnabled is true)meeting.self.videoTrack; // Video MediaStreamTrack (available when videoEnabled is true)meeting.self.screenShareTracks; // Object: { video: MediaStreamTrack, audio?: MediaStreamTrack }
// Permissions granted by usermeeting.self.mediaPermissions; // Current audio/video permissions// Media state flagsconst audioEnabled = useRealtimeKitSelector((m) => m.self.audioEnabled);const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled);const screenShareEnabled = useRealtimeKitSelector( (m) => m.self.screenShareEnabled,);
// Media tracks (MediaStreamTrack objects)const audioTrack = useRealtimeKitSelector((m) => m.self.audioTrack);const videoTrack = useRealtimeKitSelector((m) => m.self.videoTrack);const screenShareTracks = useRealtimeKitSelector( (m) => m.self.screenShareTracks,);
// Permissions granted by userconst mediaPermissions = useRealtimeKitSelector((m) => m.self.mediaPermissions);// Media state flagsmeeting.localUser.audioEnabled // Boolean: Is audio enabled?meeting.localUser.videoEnabled // Boolean: Is video enabled?meeting.localUser.screenShareEnabled // Boolean: Is screen share active?
// Permissions granted by usermeeting.localUser.isCameraPermissionGranted // Camera permission statusmeeting.localUser.isMicrophonePermissionGranted // Microphone permission status// Media state flagsmeeting.localUser.audioEnabled // Boolean: Is audio enabled?meeting.localUser.videoEnabled // Boolean: Is video enabled?meeting.localUser.screenShareEnabled // Boolean: Is screen share active?
// Permissions granted by usermeeting.localUser.isCameraPermissionGranted // Camera permission statusmeeting.localUser.isMicrophonePermissionGranted // Microphone permission status// Media state flagsconst audioEnabled = useRealtimeKitSelector((m) => m.self.audioEnabled);const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled);const screenShareEnabled = useRealtimeKitSelector( (m) => m.self.screenShareEnabled,);
// Media tracks (MediaStreamTrack objects)const audioTrack = useRealtimeKitSelector((m) => m.self.audioTrack);const videoTrack = useRealtimeKitSelector((m) => m.self.videoTrack);const screenShareTracks = useRealtimeKitSelector( (m) => m.self.screenShareTracks,);
// Permissions granted by userconst mediaPermissions = useRealtimeKitSelector((m) => m.self.mediaPermissions);// Media state flagsmeeting.localUser.audioEnabled // Boolean: Is audio enabled?meeting.localUser.videoEnabled // Boolean: Is video enabled?meeting.localUser.screenShareEnabled // Boolean: Is screen share active?
// Permissions granted by usermeeting.localUser.isCameraPermissionGranted // Camera permission statusmeeting.localUser.isMicrophonePermissionGranted // Microphone permission statusAccess room state and participant status:
// Room statemeeting.self.roomJoined; // Boolean: Has joined the meeting?meeting.self.roomState; // Current room state (see possible values below)meeting.self.isPinned; // Boolean: Is the local user pinned?
// Permissions and configmeeting.self.permissions; // Capabilities defined by presetmeeting.self.config; // Configuration for meeting appearanceRoom state values:
'init'- Initialized but not joined'joined'- Successfully joined the meeting'waitlisted'- Waiting in the waiting room'rejected'- Entry rejected'kicked'- Removed from meeting'left'- Left the meeting'ended'- Meeting has ended'disconnected'- Disconnected from meeting
// Room stateconst roomJoined = useRealtimeKitSelector((m) => m.self.roomJoined);const roomState = useRealtimeKitSelector((m) => m.self.roomState);const isPinned = useRealtimeKitSelector((m) => m.self.isPinned);
// Permissions and configconst permissions = useRealtimeKitSelector((m) => m.self.permissions);const config = useRealtimeKitSelector((m) => m.self.config);Example: Conditional rendering based on room state
const roomState = useRealtimeKitSelector((m) => m.self.roomState);
return ( <> {roomState === "disconnected" && <div>You are disconnected</div>} {roomState === "waitlisted" && <div>Waiting for host to admit you</div>} {roomState === "joined" && <div>You are in the meeting</div>} </>);Room state values:
'init'- Initialized but not joined'joined'- Successfully joined the meeting'waitlisted'- Waiting in the waiting room'rejected'- Entry rejected'kicked'- Removed from meeting'left'- Left the meeting'ended'- Meeting has ended'disconnected'- Disconnected from meeting
// Room statemeeting.localUser.roomJoined // Boolean: Has joined the meeting?meeting.localUser.waitListStatus // Waitlist status (None, Waiting, Accepted, Rejected)meeting.localUser.isPinned // Boolean: Is the local user pinned?
// Permissions and configmeeting.localUser.permissions // Capabilities defined by presetmeeting.localUser.presetName // Name of preset for local usermeeting.localUser.presetInfo // Typed object representing preset information// Room statemeeting.localUser.roomJoined // Boolean: Has joined the meeting?meeting.localUser.waitListStatus // Waitlist status (None, Waiting, Accepted, Rejected)meeting.localUser.isPinned // Boolean: Is the local user pinned?
// Permissions and configmeeting.localUser.permissions // Capabilities defined by presetmeeting.localUser.presetName // Name of preset for local usermeeting.localUser.presetInfo // Typed object representing preset information// Room stateconst roomJoined = useRealtimeKitSelector((m) => m.self.roomJoined);const roomState = useRealtimeKitSelector((m) => m.self.roomState);const isPinned = useRealtimeKitSelector((m) => m.self.isPinned);
// Permissions and configconst permissions = useRealtimeKitSelector((m) => m.self.permissions);const config = useRealtimeKitSelector((m) => m.self.config);Example: Conditional rendering based on room state
const roomState = useRealtimeKitSelector((m) => m.self.roomState);
return ( <> {roomState === "disconnected" && <Text>You are disconnected</Text>} {roomState === "waitlisted" && <Text>Waiting for host to admit you</Text>} {roomState === "joined" && <Text>You are in the meeting</Text>} </>);Room state values:
'init'- Initialized but not joined'joined'- Successfully joined the meeting'waitlisted'- Waiting in the waiting room'rejected'- Entry rejected'kicked'- Removed from meeting'left'- Left the meeting'ended'- Meeting has ended'disconnected'- Disconnected from meeting
// Room statemeeting.localUser.isHost // Boolean: Is the local user a host?meeting.localUser.isPinned // Boolean: Is the local user pinned?meeting.localUser.stageStatus // Stage status of the local user
// Permissions and flagsmeeting.localUser.flags // ParticipantFlags (recorder, hidden)Mute and unmute the microphone:
// Enable audio (unmute)await meeting.self.enableAudio();
// Disable audio (mute)await meeting.self.disableAudio();
// Check current statusconst isAudioEnabled = meeting.self.audioEnabled;import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";
function AudioControls() { const [meeting] = useRealtimeKitClient(); const audioEnabled = useRealtimeKitSelector((m) => m.self.audioEnabled);
const toggleAudio = async () => { if (audioEnabled) { await meeting.self.disableAudio(); } else { await meeting.self.enableAudio(); } };
return ( <button onClick={toggleAudio}>{audioEnabled ? "Mute" : "Unmute"}</button> );}// Enable audio (unmute)meeting.localUser.enableAudio { error: AudioError? -> }
// Disable audio (mute)meeting.localUser.disableAudio { error: AudioError? -> }
// Check current statusval isAudioEnabled = meeting.localUser.audioEnabled// Enable audio (unmute)meeting.localUser.enableAudio { err in }
// Disable audio (mute)meeting.localUser.disableAudio { err in }
// Check current statuslet isAudioEnabled = meeting.localUser.audioEnabledimport { useRealtimeKitClient, useRealtimeKitSelector } from "@cloudflare/realtimekit-react-native";import { TouchableHighlight, Text } from "react-native";
function AudioControls() { const [meeting] = useRealtimeKitClient(); const audioEnabled = useRealtimeKitSelector((m) => m.self.audioEnabled);
const toggleAudio = async () => { if (audioEnabled) { await meeting.self.disableAudio(); } else { await meeting.self.enableAudio(); } };
return ( <TouchableHighlight onPress={toggleAudio}> <Text>{audioEnabled ? "Mute" : "Unmute"}</Text> </TouchableHighlight> );}// Enable audio (unmute)meeting.localUser.enableAudio(onResult: (e) { // handle error if any});
// Disable audio (mute)meeting.localUser.disableAudio(onResult: (e) { // handle error if any});
// Check current statusfinal isAudioEnabled = meeting.localUser.audioEnabled;Enable and disable the camera:
// Enable videoawait meeting.self.enableVideo();
// Disable videoawait meeting.self.disableVideo();
// Check current statusconst isVideoEnabled = meeting.self.videoEnabled;function VideoControls() { const [meeting] = useRealtimeKitClient(); const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled);
const toggleVideo = async () => { if (videoEnabled) { await meeting.self.disableVideo(); } else { await meeting.self.enableVideo(); } };
return ( <button onClick={toggleVideo}> {videoEnabled ? "Stop Video" : "Start Video"} </button> );}// Enable videomeeting.localUser.enableVideo { error: VideoError? -> }
// Disable videomeeting.localUser.disableVideo { error: VideoError? -> }
// Check current statusval isVideoEnabled = meeting.localUser.videoEnabled// Enable videomeeting.localUser.enableVideo { err in }
// Disable videomeeting.localUser.disableVideo { err in }
// Check current statuslet isVideoEnabled = meeting.localUser.videoEnabledfunction VideoControls() { const [meeting] = useRealtimeKitClient(); const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled);
const toggleVideo = async () => { if (videoEnabled) { await meeting.self.disableVideo(); } else { await meeting.self.enableVideo(); } };
return ( <TouchableHighlight onPress={toggleVideo}> <Text>{videoEnabled ? "Stop Video" : "Start Video"}</Text> </TouchableHighlight> );}// Enable videomeeting.localUser.enableVideo(onResult: (e) { // handle error if any});
// Disable videomeeting.localUser.disableVideo(onResult: (e) { // handle error if any});
// Check current statusfinal isVideoEnabled = meeting.localUser.videoEnabled;Start and stop screen sharing:
// Enable screen shareawait meeting.self.enableScreenShare();
// Disable screen shareawait meeting.self.disableScreenShare();
// Check current statusconst isScreenShareEnabled = meeting.self.screenShareEnabled;function ScreenShareControls() { const [meeting] = useRealtimeKitClient(); const screenShareEnabled = useRealtimeKitSelector( (m) => m.self.screenShareEnabled, );
const toggleScreenShare = async () => { if (screenShareEnabled) { await meeting.self.disableScreenShare(); } else { await meeting.self.enableScreenShare(); } };
return ( <button onClick={toggleScreenShare}> {screenShareEnabled ? "Stop Sharing" : "Share Screen"} </button> );}// Enable screen sharemeeting.localUser.enableScreenShare()
// Disable screen sharemeeting.localUser.disableScreenShare()
// Check current statusval isScreenShareEnabled = meeting.localUser.screenShareEnabled// Enable screen sharelet err: ScreenShareError? = meeting.localUser.enableScreenShare()
// Disable screen sharemeeting.localUser.disableScreenShare()Refer to the Screen Share Setup (iOS) section for platform-specific configuration.
function ScreenShareControls() { const [meeting] = useRealtimeKitClient(); const screenShareEnabled = useRealtimeKitSelector( (m) => m.self.screenShareEnabled, );
const toggleScreenShare = async () => { if (screenShareEnabled) { await meeting.self.disableScreenShare(); } else { await meeting.self.enableScreenShare(); } };
return ( <TouchableHighlight onPress={toggleScreenShare}> <Text>{screenShareEnabled ? "Stop Sharing" : "Share Screen"}</Text> </TouchableHighlight> );}// Enable screen sharemeeting.localUser.enableScreenShare();
// Disable screen sharemeeting.localUser.disableScreenShare();Update the display name before joining the meeting:
await meeting.self.setName("New Name");await meeting.self.setName("New Name");meeting.localUser.setDisplayName("New Name")meeting.localUser.setDisplayName(name: "New Name")await meeting.self.setName("New Name");if (meeting.permissions.miscellaneous.canEditDisplayName) { meeting.localUser.setDisplayName("New Name");}// Get all media devicesconst devices = await meeting.self.getAllDevices();
// Get all audio input devices (microphones)const audioDevices = await meeting.self.getAudioDevices();
// Get all video input devices (cameras)const videoDevices = await meeting.self.getVideoDevices();
// Get all audio output devices (speakers)const speakerDevices = await meeting.self.getSpeakerDevices();
// Get device by IDconst device = await meeting.self.getDeviceById("device-id", "audio");
// Get current devices being usedconst currentDevices = meeting.self.getCurrentDevices();// Returns: { audio: MediaDeviceInfo, video: MediaDeviceInfo, speaker: MediaDeviceInfo }import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";import { useState, useEffect } from "react";
function DeviceSelector() { const [meeting] = useRealtimeKitClient(); const [audioDevices, setAudioDevices] = useState([]); const [videoDevices, setVideoDevices] = useState([]);
useEffect(() => { if (!meeting) return;
const loadDevices = async () => { const audio = await meeting.self.getAudioDevices(); const video = await meeting.self.getVideoDevices(); setAudioDevices(audio); setVideoDevices(video); };
loadDevices(); }, [meeting]);
const handleDeviceChange = async (device) => { await meeting.self.setDevice(device); };
return ( <div> <select onChange={(e) => { const device = audioDevices.find( (d) => d.deviceId === e.target.value, ); handleDeviceChange(device); }} > {audioDevices.map((device) => ( <option key={device.deviceId} value={device.deviceId}> {device.label} </option> ))} </select> </div> );}Get current devices being used:
const currentDevices = meeting.self.getCurrentDevices();// Returns: { audio: MediaDeviceInfo, video: MediaDeviceInfo, speaker: MediaDeviceInfo }// Get all audio devicesval audioDevices: List<AudioDevice> = meeting.localUser.getAudioDevices()
// Get all video devicesval videoDevices: List<VideoDevice> = meeting.localUser.getVideoDevices()
// Get currently selected audio deviceval selectedAudioDevice: AudioDevice = meeting.localUser.getSelectedAudioDevice()
// Get currently selected video deviceval selectedVideoDevice: VideoDevice = meeting.localUser.getSelectedVideoDevice()// Get all audio deviceslet audioDevices = meeting.localUser.getAudioDevices()
// Get all video deviceslet videoDevices = meeting.localUser.getVideoDevices()
// Get currently selected audio devicelet selectedAudioDevice = meeting.localUser.getSelectedAudioDevice()
// Get currently selected video devicelet selectedVideoDevice = meeting.localUser.getSelectedVideoDevice()import { useRealtimeKitClient } from "@cloudflare/realtimekit-react-native";import { useState, useEffect } from "react";import { FlatList, TouchableHighlight, Text, View } from "react-native";
function DeviceSelector() { const [meeting] = useRealtimeKitClient(); const [audioDevices, setAudioDevices] = useState([]); const [videoDevices, setVideoDevices] = useState([]);
useEffect(() => { if (!meeting) return;
const loadDevices = async () => { const audio = await meeting.self.getAudioDevices(); const video = await meeting.self.getVideoDevices(); setAudioDevices(audio); setVideoDevices(video); };
loadDevices(); }, [meeting]);
const handleDeviceChange = async (device) => { await meeting.self.setDevice(device); };
return ( <View> <FlatList data={audioDevices} renderItem={({item}) => <TouchableHighlight onPress={() => handleDeviceChange(item)}> <Text>{item.label}</Text> </TouchableHighlight> } keyExtractor={item => item.deviceId} /> </View> );}Get current devices being used:
const currentDevices = meeting.self.getCurrentDevices();// Returns: { audio: MediaDeviceInfo, video: MediaDeviceInfo, speaker: MediaDeviceInfo }// Get all audio devicesfinal audioDevices = await meeting.localUser.getAudioDevices();
// Get all video devicesfinal videoDevices = await meeting.localUser.getVideoDevices();
// Get currently selected audio devicefinal selectedAudioDevice = meeting.localUser.getSelectedAudioDevice();
// Get currently selected video devicefinal selectedVideoDevice = meeting.localUser.getSelectedVideoDevice();Switch to a different media device:
// Get all devicesconst devices = await meeting.self.getAllDevices();
// Set a specific device (replaces device of the same kind)await meeting.self.setDevice(devices[0]);Use the device selector example from the previous section. The handleDeviceChange function demonstrates how to switch devices.
// Get all audio devicesval audioDevices = meeting.localUser.getAudioDevices()
// Set audio devicemeeting.localUser.setAudioDevice(audioDevices[0])
// Get all video devicesval videoDevices = meeting.localUser.getVideoDevices()
// Set video devicemeeting.localUser.setVideoDevice(videoDevices[0])
// Switch between front and back camera on devices with 2 camerasmeeting.localUser.switchCamera()// Set audio devicemeeting.localUser.setAudioDevice(device)
// Set video devicemeeting.localUser.setVideoDevice(videoDevice: device)
// Switch between front and back camerameeting.localUser.switchCamera()Use the device selector example from the previous section. The handleDeviceChange function demonstrates how to switch devices.
const handleDeviceChange = async (device) => { await meeting.self.setDevice(device);};// Get all available audio devicesfinal audioDevices = await meeting.localUser.getAudioDevices();
// Switch audio deviceawait meeting.localUser.setAudioDevice(audioDevices[1]);
// Get all available video devicesfinal videoDevices = await meeting.localUser.getVideoDevices();
// Switch video deviceawait meeting.localUser.setVideoDevice(videoDevices[1]);
// Switch between available camera sourcesmeeting.localUser.switchCamera();Attach the local video track to a <video> element:
<video id="local-video" autoplay playsinline></video>const videoElement = document.getElementById("local-video");
// Register the video element to display videomeeting.self.registerVideoElement(videoElement);
// For local preview (not sent to other users), pass true as second argumentmeeting.self.registerVideoElement(videoElement, true);Remove the video element when no longer needed:
meeting.self.deregisterVideoElement(videoElement);Display local video with the UI Kit video tile component:
import { RtkParticipantTile } from "@cloudflare/realtimekit-react-ui";import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react";
function LocalVideo() { const localUser = useRealtimeKitSelector((m) => m.self);
return <RtkParticipantTile participant={localUser} />;}Create custom video element implementations:
import { useRealtimeKitClient, useRealtimeKitSelector,} from "@cloudflare/realtimekit-react";import { useEffect, useRef } from "react";
function LocalVideoCustom() { const [meeting] = useRealtimeKitClient(); const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled); const videoTrack = useRealtimeKitSelector((m) => m.self.videoTrack); const videoRef = useRef(null);
useEffect(() => { if (!videoRef.current || !meeting) return;
// Register video element meeting.self.registerVideoElement(videoRef.current);
return () => { // Cleanup: deregister on unmount meeting.self.deregisterVideoElement(videoRef.current); }; }, [meeting]);
return ( <video ref={videoRef} autoPlay playsInline muted style={{ display: videoEnabled ? "block" : "none" }} /> );}Retrieve a self-preview video view that renders the local camera stream:
// Get the self-preview video viewval videoView = meeting.localUser.getSelfPreview()For rendering other participants' video, use:
// Get video view for camera streamval participantVideoView = participant.getVideoView()
// Get video view for screenshare streamval screenshareView = participant.getScreenShareVideoView()Control video rendering with lifecycle methods:
// Start rendering videovideoView.renderVideo()
// Stop rendering video (but keep the view)videoView.stopVideoRender()
// Release native resources when donevideoView.release()import android.os.Bundleimport android.widget.FrameLayoutimport androidx.appcompat.app.AppCompatActivityimport io.dyte.core.VideoView
class MainActivity : AppCompatActivity() { private lateinit var videoView: VideoView
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)
// Get the self-preview video view videoView = meeting.localUser.getSelfPreview()
// Add to your layout val container = findViewById<FrameLayout>(R.id.video_container) container.addView(videoView)
// Start rendering videoView.renderVideo() }
override fun onPause() { super.onPause() // Stop rendering when activity is paused videoView.stopVideoRender() }
override fun onResume() { super.onResume() // Resume rendering when activity is resumed videoView.renderVideo() }
override fun onDestroy() { super.onDestroy() // Clean up resources videoView.release() }}Retrieve video views that render the participant's video streams:
// Get video view for local camera streamlet videoView = meeting.localUser.getVideoView()
// Get video view for screenshare streamlet screenshareView = meeting.localUser.getScreenShareVideoView()The UIView handles its own lifecycle automatically and cleans up native resources when it exits the current window. No manual cleanup is required.
import UIKitimport RealtimeKit
class VideoViewController: UIViewController { private var videoView: UIView?
override func viewDidLoad() { super.viewDidLoad()
// Get the video view for local camera videoView = meeting.localUser.getVideoView()
// Add to your view hierarchy if let videoView = videoView { videoView.frame = view.bounds videoView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(videoView) } }}For screenshare:
// Get and display screenshare viewlet screenshareView = meeting.localUser.getScreenShareVideoView()if let screenshareView = screenshareView { screenshareView.frame = view.bounds screenshareView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(screenshareView)}import React from "react";import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react-native";import { MediaStream, RTCView } from "@cloudflare/react-native-webrtc";
export default function VideoView() { const { videoTrack } = useRealtimeKitSelector( (m) => m.participants.active, ).toArray()[0]; const stream = new MediaStream(undefined); stream.addTrack(videoTrack); return ( <RTCView objectFit={"cover"} style={{ flex: 1 }} streamURL={stream.toURL()} mirror={true} zOrder={1} /> );}Display video streams with the VideoView widget:
import 'package:realtimekit_core/realtimekit_core.dart';import 'package:flutter/material.dart';
class LocalVideoView extends StatelessWidget { final RtkMeetingParticipant localUser;
const LocalVideoView({Key? key, required this.localUser}) : super(key: key);
@override Widget build(BuildContext context) { return VideoView( meetingParticipant: localUser, isSelfParticipant: true, ); }}The VideoView widget accepts the following parameters:
meetingParticipant(required): TheRtkMeetingParticipantwhose video should be displayedisSelfParticipant(optional): Set totruefor the local participant's self-preview, defaults tofalsekey(optional): Widget key for Flutter's widget tree management
import 'package:flutter/material.dart';import 'package:realtimekit_core/realtimekit_core.dart';
class MeetingScreen extends StatelessWidget { final RtkMeeting meeting;
const MeetingScreen({Key? key, required this.meeting}) : super(key: key);
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Video Preview')), body: Container( child: VideoView( meetingParticipant: meeting.localUser, isSelfParticipant: true, ), ), ); }}For displaying other participants' video:
// Display remote participant videoVideoView( meetingParticipant: remoteParticipant, isSelfParticipant: false,)The VideoView widget automatically handles video rendering and resource cleanup based on Flutter's widget lifecycle.
In Xcode, add a Broadcast Upload Extension through File → New → Target. Choose iOS → Broadcast Upload Extension and fill out the required information.
Add your extension to an app group:
- Go to your extension's target in the project
- In the Signings & Capabilities tab, click the + button in the top left
- Add App Groups
- Add App Groups to your main app as well, ensuring the App Group identifier is the same for both
Edit your SampleHandler class:
import RealtimeKit
class SampleHandler: RtkSampleHandler {}Ensure both App and Extension Info.plist files contain these keys:
<key>RTKRTCAppGroupIdentifier</key><string>(name of the group you have created)</string>Add this key inside the Info.plist of the main App:
<key>RTKRTCScreenSharingExtension</key><string>(Bundle Identifier of the Broadcast upload extension)</string>Launch the broadcast extension and enable screen share:
meeting.localUser.enableScreenShare()To stop the screen share:
meeting.localUser.disableScreenShare()In Xcode, add a Broadcast Upload Extension through File → New → Target. Choose iOS → Broadcast Upload Extension and fill out the required information.
Add your extension to an app group:
- Go to your extension's target in the project
- In the Signings & Capabilities tab, click the + button in the top left
- Add App Groups
- Add App Groups to your main app as well, ensuring the App Group identifier is the same for both
- Place the
RtkSampleHandler.swiftfile from GitHub ↗ in theios/<screenshare-folder>/folder - Create or replace
SampleHandler.swift:
import ReplayKit
class SampleHandler: RtkSampleHandler {}Ensure both App and Extension Info.plist files contain these keys:
<key>RTKRTCAppGroupIdentifier</key><string>(name of the group you have created)</string>Add this key inside the Info.plist of the main App:
<key>RTKRTCScreenSharingExtension</key><string>(Bundle Identifier of the Broadcast upload extension)</string>Launch the broadcast extension and enable screen share:
meeting.localUser.enableScreenShare()To stop the screen share:
meeting.localUser.disableScreenShare()In Xcode, add a Broadcast Upload Extension through File → New → Target. Choose iOS → Broadcast Upload Extension and fill out the required information.
Add your extension to an app group:
- Go to your extension's target in the project
- In the Signings & Capabilities tab, click the + button in the top left
- Add App Groups
- Add App Groups to your main app as well, ensuring the App Group identifier is the same for both
Edit your SampleHandler class:
import RealtimeKitCore
class SampleHandler: RTKScreenshareHandler { override init() { super.init(appGroupIdentifier: "<YOUR_APP_GROUP_IDENTIFIER>", bundleIdentifier: "<YOUR_APP_BUNDLE_IDENTIFIER>") }}Ensure both App and Extension Info.plist files contain these keys:
<key>RTCAppGroupIdentifier</key><string>(YOUR_APP_GROUP_IDENTIFIER)</string>Add this key inside the Info.plist of the main App:
<key>RTCAppScreenSharingExtension</key><string>(Bundle Identifier of the Broadcast upload extension)</string>Launch the broadcast extension and enable screen share:
meeting.self.enableScreenShare()To stop the screen share:
meeting.self.disableScreenShare()Fires when the local user joins the meeting:
meeting.self.on("roomJoined", () => { console.log("Successfully joined the meeting");});const roomJoined = useRealtimeKitSelector((m) => m.self.roomJoined);
useEffect(() => { if (roomJoined) { console.log("Successfully joined the meeting"); }}, [roomJoined]);Or use event listener:
useEffect(() => { if (!meeting) return;
const handleRoomJoined = () => { console.log("Successfully joined the meeting"); };
meeting.self.on("roomJoined", handleRoomJoined);
return () => { meeting.self.off("roomJoined", handleRoomJoined); };}, [meeting]);Android SDK uses a different event model. Monitor roomJoined property changes or use listeners for state changes.
iOS SDK uses a different event model. Monitor roomJoined property changes or use listeners for state changes.
const roomJoined = useRealtimeKitSelector((m) => m.self.roomJoined);
useEffect(() => { if (roomJoined) { console.log("Successfully joined the meeting"); }}, [roomJoined]);Or use event listener:
useEffect(() => { if (!meeting) return;
const handleRoomJoined = () => { console.log("Successfully joined the meeting"); };
meeting.self.on("roomJoined", handleRoomJoined);
return () => { meeting.self.off("roomJoined", handleRoomJoined); };}, [meeting]);Flutter SDK uses a different event model. Monitor roomJoined property changes or use listeners for state changes.
Fires when the local user leaves the meeting:
meeting.self.on("roomLeft", ({ state }) => { console.log("Left the meeting with state:", state);
// Handle different leave states if (state === "left") { console.log("User voluntarily left"); } else if (state === "kicked") { console.log("User was kicked from the meeting"); } else if (state === "ended") { console.log("Meeting has ended"); } else if (state === "disconnected") { console.log("Lost connection to meeting"); }});Possible state values: 'left', 'kicked', 'ended', 'rejected', 'disconnected', 'failed'
const roomJoined = useRealtimeKitSelector((m) => m.self.roomJoined);
useEffect(() => { if (!roomJoined) { console.log("Left the meeting"); }}, [roomJoined]);Or use event listener for detailed state:
meeting.self.on("roomLeft", ({ state }) => { if (state === "left") { console.log("User voluntarily left"); } else if (state === "kicked") { console.log("User was kicked"); }});Use RtkSelfEventListener to monitor when the local user is removed from the meeting:
meeting.addSelfEventListener(object : RtkSelfEventListener { override fun onRemovedFromMeeting() { // display alert that user is no longer in the meeting }})iOS SDK uses a different event model. Monitor roomJoined property changes or use listeners for state changes.
const roomJoined = useRealtimeKitSelector((m) => m.self.roomJoined);
useEffect(() => { if (!roomJoined) { console.log("Left the meeting"); }}, [roomJoined]);Or use event listener for detailed state:
meeting.self.on("roomLeft", ({ state }) => { if (state === "left") { console.log("User voluntarily left"); } else if (state === "kicked") { console.log("User was kicked"); }});class MeetingSelfListener extends RtkSelfEventListener { @override void onRemovedFromMeeting() { // User was removed from the meeting (kicked or meeting ended) // Display alert or navigate to exit screen }}
// Add the listenermeeting.addSelfEventListener(MeetingSelfListener());Fires when video is enabled or disabled:
meeting.self.on("videoUpdate", ({ videoEnabled, videoTrack }) => { console.log("Video state:", videoEnabled);
if (videoEnabled) { // Video track is available, can display it const videoElement = document.getElementById("my-video"); const stream = new MediaStream(); stream.addTrack(videoTrack); videoElement.srcObject = stream; videoElement.play(); }});const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled);const videoTrack = useRealtimeKitSelector((m) => m.self.videoTrack);
useEffect(() => { if (videoEnabled && videoTrack) { console.log("Video is enabled"); // Handle video track }}, [videoEnabled, videoTrack]);meeting.addSelfEventListener(object : RtkSelfEventListener { override fun onVideoUpdate(isEnabled: Boolean) { if (isEnabled) { // video is enabled, other participants can see local user } else { // video is disabled, other participants cannot see local user } }})extension MeetingViewModel: RtkSelfEventListener { func onVideoUpdate(isEnabled: Bool) { if (isEnabled) { // video is enabled, other participants can see local user } else { // video is disabled, other participants cannot see local user } }}const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled);const videoTrack = useRealtimeKitSelector((m) => m.self.videoTrack);
useEffect(() => { if (videoEnabled && videoTrack) { console.log("Video is enabled"); // Handle video track }}, [videoEnabled, videoTrack]);Flutter SDK uses a different event model. Monitor videoEnabled property changes.
Fires when audio is enabled or disabled:
meeting.self.on("audioUpdate", ({ audioEnabled, audioTrack }) => { console.log("Audio state:", audioEnabled);
if (audioEnabled) { // Audio track is available console.log("Microphone is on"); }});const audioEnabled = useRealtimeKitSelector((m) => m.self.audioEnabled);const audioTrack = useRealtimeKitSelector((m) => m.self.audioTrack);
useEffect(() => { if (audioEnabled && audioTrack) { console.log("Audio is enabled"); // Handle audio track }}, [audioEnabled, audioTrack]);meeting.addSelfEventListener(object : RtkSelfEventListener { override fun onAudioUpdate(isEnabled: Boolean) { if (isEnabled) { // audio is enabled, other participants can hear local user } else { // audio is disabled, other participants cannot hear local user } }})extension MeetingViewModel: RtkSelfEventListener { func onAudioUpdate(isEnabled: Bool) { if (isEnabled) { // audio is enabled, other participants can hear local user } else { // audio is disabled, other participants cannot hear local user } }}const audioEnabled = useRealtimeKitSelector((m) => m.self.audioEnabled);const audioTrack = useRealtimeKitSelector((m) => m.self.audioTrack);
useEffect(() => { if (audioEnabled && audioTrack) { console.log("Audio is enabled"); // Handle audio track }}, [audioEnabled, audioTrack]);Flutter SDK uses a different event model. Monitor audioEnabled property changes.
Fires when screen sharing starts or stops:
meeting.self.on( "screenShareUpdate", ({ screenShareEnabled, screenShareTracks }) => { console.log("Screen share state:", screenShareEnabled);
if (screenShareEnabled) { // Screen share tracks are available const screenElement = document.getElementById("my-screen-share"); const stream = new MediaStream(); stream.addTrack(screenShareTracks.video); if (screenShareTracks.audio) { stream.addTrack(screenShareTracks.audio); } screenElement.srcObject = stream; screenElement.play(); } },);const screenShareEnabled = useRealtimeKitSelector( (m) => m.self.screenShareEnabled,);const screenShareTracks = useRealtimeKitSelector( (m) => m.self.screenShareTracks,);
useEffect(() => { if (screenShareEnabled && screenShareTracks) { console.log("Screen sharing is active"); // Handle screen share tracks }}, [screenShareEnabled, screenShareTracks]);meeting.addSelfEventListener(object : RtkSelfEventListener { override fun onScreenShareStartFailed(reason: String) { // screen share failed to start }
override fun onScreenShareUpdate(isEnabled: Boolean) { if (isEnabled) { // screen share is enabled } else { // screen share is disabled } }})meeting.addSelfEventListener(self)
extension MeetingViewModel: RtkSelfEventListener { func onRemovedFromMeeting() { // User was removed from the meeting (kicked or meeting ended) // Display alert or navigate to exit screen }
func onMeetingRoomDisconnected() { // Lost connection to the meeting room // Display reconnection UI or error message }}You can also monitor the roomJoined property for state changes:
let isInMeeting = meeting.localUser.roomJoinedconst screenShareEnabled = useRealtimeKitSelector( (m) => m.self.screenShareEnabled,);const screenShareTracks = useRealtimeKitSelector( (m) => m.self.screenShareTracks,);
useEffect(() => { if (screenShareEnabled && screenShareTracks) { console.log("Screen sharing is active"); // Handle screen share tracks }}, [screenShareEnabled, screenShareTracks]);Flutter SDK uses a different event model. Monitor screenShareEnabled property changes.
Fires when the active device changes:
meeting.self.on("deviceUpdate", ({ device }) => { // Handle device change if (device.kind === "audioinput") { console.log("Microphone changed:", device.label); } else if (device.kind === "videoinput") { console.log("Camera changed:", device.label); } else if (device.kind === "audiooutput") { console.log("Speaker changed:", device.label); }});useEffect(() => { if (!meeting) return;
const handleDeviceUpdate = ({ device }) => { if (device.kind === "audioinput") { console.log("Microphone changed:", device.label); } else if (device.kind === "videoinput") { console.log("Camera changed:", device.label); } };
meeting.self.on("deviceUpdate", handleDeviceUpdate);
return () => { meeting.self.off("deviceUpdate", handleDeviceUpdate); };}, [meeting]);meeting.self.addSelfEventListener(object : RtkSelfEventListener() { override fun onAudioDeviceChanged(device: AudioDevice) { // Handle audio device change println("Audio device changed: ${device.label}") }
override fun onVideoDeviceChanged(device: VideoDevice) { // Handle video device change println("Video device changed: ${device.label}") }})meeting.self.addSelfEventListener(self)
// RtkSelfEventListener implementationfunc onAudioDeviceChanged(device: AudioDevice) { // Handle audio device change print("Audio device changed: \(device.label)")}
func onVideoDeviceChanged(device: VideoDevice) { // Handle video device change print("Video device changed: \(device.label)")}useEffect(() => { if (!meeting) return;
const handleDeviceUpdate = ({ device }) => { if (device.kind === "audioinput") { console.log("Microphone changed:", device.label); } else if (device.kind === "videoinput") { console.log("Camera changed:", device.label); } };
meeting.self.on("deviceUpdate", handleDeviceUpdate);
return () => { meeting.self.off("deviceUpdate", handleDeviceUpdate); };}, [meeting]);class DeviceChangeListener extends RtkSelfEventListener { @override void onAudioDeviceChanged(AudioDevice audioDevice) { // Handle audio device change print('Audio device changed: ${audioDevice.label}'); }
@override void onVideoDeviceChanged(VideoDevice videoDevice) { // Handle video device change print('Video device changed: ${videoDevice.label}'); }}
// Add the listenermeeting.addSelfEventListener(DeviceChangeListener());Triggered when the list of available devices changes (device plugged in or out):
meeting.self.on("deviceListUpdate", ({ added, removed, devices }) => { console.log("Device list updated"); console.log("Added devices:", added); console.log("Removed devices:", removed); console.log("All devices:", devices);});useEffect(() => { if (!meeting) return;
const handleDeviceListUpdate = ({ added, removed, devices }) => { console.log("Device list updated"); console.log("Added devices:", added); console.log("Removed devices:", removed); console.log("All devices:", devices); };
meeting.self.on("deviceListUpdate", handleDeviceListUpdate);
return () => { meeting.self.off("deviceListUpdate", handleDeviceListUpdate); };}, [meeting]);meeting.addSelfEventListener(object : RtkSelfEventListener { // Triggered when audio devices are added or removed override fun onAudioDevicesUpdated() { val audioDevices = meeting.localUser.getAudioDevices() // Update UI with new audio device list }})meeting.addSelfEventListener(object: RtkSelfEventListener { // Triggered when audio devices are added or removed func onAudioDevicesUpdated() { let audioDevices = meeting.localUser.getAudioDevices() // Update UI with new audio device list }})useEffect(() => { if (!meeting) return;
const handleDeviceListUpdate = ({ added, removed, devices }) => { console.log("Device list updated"); console.log("Added devices:", added); console.log("Removed devices:", removed); console.log("All devices:", devices); };
meeting.self.on("deviceListUpdate", handleDeviceListUpdate);
return () => { meeting.self.off("deviceListUpdate", handleDeviceListUpdate); };}, [meeting]);class DeviceListListener extends RtkSelfEventListener { final RealtimekitClient meeting;
DeviceListListener(this.meeting);
@override void onAudioDevicesUpdated(List<AudioDevice> devices) { // Triggered when audio devices are added or removed // Update UI with new audio device list }
@override void onVideoDeviceChanged(VideoDevice videoDevice) { // Handle video device change print('Video device changed to: ${videoDevice.label}'); }}
// Add the listenermeeting.addSelfEventListener(DeviceListListener(meeting));Monitor your own network quality:
meeting.self.on( "mediaScoreUpdate", ({ kind, isScreenshare, score, scoreStats }) => { if (kind === "video") { console.log( `Your ${isScreenshare ? "screenshare" : "video"} quality score is`, score, ); }
if (kind === "audio") { console.log("Your audio quality score is", score); }
if (score < 5) { console.log("Your media quality is poor"); } },);The scoreStats object provides detailed statistics:
// Audio Producer{ "kind": "audio", "isScreenshare": false, "score": 10, "participantId": "meeting.self.id", "scoreStats": { "score": 10, "bitrate": 22452, "packetsLostPercentage": 0, "jitter": 0, "isScreenShare": false }}
// Video Producer{ "kind": "video", "isScreenshare": false, "score": 10, "participantId": "meeting.self.id", "scoreStats": { "score": 10, "frameWidth": 640, "frameHeight": 480, "framesPerSecond": 24, "jitter": 0, "isScreenShare": false, "packetsLostPercentage": 0, "bitrate": 576195, "cpuLimitations": false, "bandwidthLimitations": false }}useEffect(() => { if (!meeting) return;
const handleMediaScoreUpdate = ({ kind, isScreenshare, score, scoreStats, }) => { if (kind === "video") { console.log( `Your ${isScreenshare ? "screenshare" : "video"} quality score is`, score, ); }
if (score < 5) { console.log("Your media quality is poor"); } };
meeting.self.on("mediaScoreUpdate", handleMediaScoreUpdate);
return () => { meeting.self.off("mediaScoreUpdate", handleMediaScoreUpdate); };}, [meeting]);Android SDK does not currently expose network quality scores.
iOS SDK does not currently expose network quality scores.
useEffect(() => { if (!meeting) return;
const handleMediaScoreUpdate = ({ kind, isScreenshare, score, scoreStats, }) => { if (kind === "video") { console.log( `Your ${isScreenshare ? "screenshare" : "video"} quality score is`, score, ); }
if (score < 5) { console.log("Your media quality is poor"); } };
meeting.self.on("mediaScoreUpdate", handleMediaScoreUpdate);
return () => { meeting.self.off("mediaScoreUpdate", handleMediaScoreUpdate); };}, [meeting]);Flutter SDK does not currently expose network quality scores.
Triggered when permissions are updated dynamically:
// Listen to specific permission updatesmeeting.self.permissions.on("chatUpdate", () => { console.log("Chat permissions updated"); // Check meeting.self.permissions for updated permissions});
meeting.self.permissions.on("pollsUpdate", () => { console.log("Polls permissions updated");});
meeting.self.permissions.on("pluginsUpdate", () => { console.log("Plugins permissions updated");});
// Listen to all permission updatesmeeting.self.permissions.on("*", () => { console.log("Permissions updated");});Monitor permissions using selectors:
const permissions = useRealtimeKitSelector((m) => m.self.permissions);
useEffect(() => { console.log("Permissions updated:", permissions);}, [permissions]);Android SDK uses a different permissions model. Refer to the Android-specific documentation.
iOS SDK uses a different permissions model. Refer to the iOS-specific documentation.
Monitor permissions using selectors:
const permissions = useRealtimeKitSelector((m) => m.self.permissions);
useEffect(() => { console.log("Permissions updated:", permissions);}, [permissions]);Flutter SDK uses a different permissions model. Refer to the Flutter-specific documentation.
Triggered when media permissions are denied or media capture fails:
meeting.self.on("mediaPermissionError", ({ message, kind }) => { console.log(`Failed to capture ${kind}: ${message}`);
// Handle different error types if (message === "DENIED") { console.log("User denied permission"); } else if (message === "SYSTEM_DENIED") { console.log("System denied permission"); } else if (message === "COULD_NOT_START") { console.log("Failed to start media stream"); }});Possible values:
message:'DENIED','SYSTEM_DENIED','COULD_NOT_START'kind:'audio','video','screenshare'
useEffect(() => { if (!meeting) return;
const handlePermissionError = ({ message, kind }) => { console.log(`Failed to capture ${kind}: ${message}`);
if (message === "DENIED") { // Show UI to guide user to grant permissions } };
meeting.self.on("mediaPermissionError", handlePermissionError);
return () => { meeting.self.off("mediaPermissionError", handlePermissionError); };}, [meeting]);meeting.addSelfEventListener(object : RtkSelfEventListener { override fun onMeetingRoomJoinedWithoutCameraPermission() { // meeting joined without camera permission }
override fun onMeetingRoomJoinedWithoutMicPermission() { // meeting joined without microphone permission }})meeting.addSelfEventListener(self)
extension MeetingViewModel: RtkSelfEventListener { func onMeetingRoomJoinedWithoutCameraPermission() { // meeting joined without camera permission }
func onMeetingRoomJoinedWithoutMicPermission() { // meeting joined without microphone permission }}You can also check permission status using properties:
let hasCameraPermission = meeting.localUser.isCameraPermissionGrantedlet hasMicPermission = meeting.localUser.isMicrophonePermissionGranteduseEffect(() => { if (!meeting) return;
const handlePermissionError = ({ message, kind }) => { console.log(`Failed to capture ${kind}: ${message}`);
if (message === "DENIED") { // Show UI to guide user to grant permissions } };
meeting.self.on("mediaPermissionError", handlePermissionError);
return () => { meeting.self.off("mediaPermissionError", handlePermissionError); };}, [meeting]);class PermissionListener extends RtkSelfEventListener { @override void onMeetingRoomJoinedWithoutCameraPermission() { // Meeting joined without camera permission }
@override void onMeetingRoomJoinedWithoutMicPermission() { // Meeting joined without microphone permission }}
// Add the listenermeeting.addSelfEventListener(PermissionListener());You can also check permission status using properties:
final hasCameraPermission = meeting.localUser.isCameraPermissionGranted;final hasMicPermission = meeting.localUser.isMicrophonePermissionGranted;For meetings with waiting room enabled:
Monitor the roomState property for waitlist status. The value 'waitlisted' indicates the user is in the waiting room.
const roomState = useRealtimeKitSelector((m) => m.self.roomState);
useEffect(() => { if (roomState === "waitlisted") { console.log("Waiting for host to admit you"); }}, [roomState]);// Get current waitlist statusval waitListStatus = meeting.localUser.waitListStatus
// Listen to waitlist status changesmeeting.addSelfEventListener(object : RtkSelfEventListener { override fun onWaitListStatusUpdate(waitListStatus: WaitListStatus) { // handle waitlist status here }})// Get current waitlist statuslet waitListStatus = meeting.localUser.waitListStatus
// Listen to waitlist status changesextension MeetingViewModel: RtkSelfEventListener { func onWaitlistedUpdate() { // handle waitlist update }}const roomState = useRealtimeKitSelector((m) => m.self.roomState);
useEffect(() => { if (roomState === "waitlisted") { console.log("Waiting for host to admit you"); }}, [roomState]);Flutter SDK uses a different event model. Monitor stageStatus or relevant properties for waitlist status.
The iOS SDK provides additional platform-specific events:
Triggered when the proximity sensor detects a change (useful for earpiece detection):
extension MeetingViewModel: RtkSelfEventListener { func onProximityChanged() { // Handle proximity sensor change // Useful for detecting when device is near user's ear }}For webinar-specific functionality:
extension MeetingViewModel: RtkSelfEventListener { func onWebinarPresentRequestReceived() { // Handle request to present in webinar }
func onStoppedPresenting() { // Handle stopped presenting in webinar }}Listen to broadcast messages in the room:
extension MeetingViewModel: RtkSelfEventListener { func onRoomMessage() { // Handle room broadcast message }}Pin or unpin yourself in the meeting (requires appropriate permissions):
Web SDK does not currently support pinning the local participant.
Web SDK does not currently support pinning the local participant.
Android SDK does not currently support pinning the local participant.
// Pin yourselfmeeting.localUser.pin()
// Unpin yourselfmeeting.localUser.unpin()
// Check if pinnedlet isPinned = meeting.localUser.isPinned// Pin yourselfawait meeting.self.pin();
// Unpin yourselfawait meeting.self.unpin();
// Check if pinnedconst isPinned = meeting.self.isPinned;Flutter SDK does not currently support pinning the local participant.
Update video or screenshare resolution at runtime:
Web SDK does not currently expose runtime constraint updates for local participant.
Web SDK does not currently expose runtime constraint updates for local participant.
Android SDK does not currently expose runtime constraint updates.
iOS SDK does not currently expose runtime constraint updates.
Update camera resolution while already streaming:
meeting.self.updateVideoConstraints({ width: { ideal: 1920 }, height: { ideal: 1080 },});Update screenshare resolution while already streaming:
meeting.self.updateScreenshareConstraints({ width: { ideal: 1920 }, height: { ideal: 1080 },});Flutter SDK does not currently expose runtime constraint updates.
Was this helpful?
- Resources
- API
- New to Cloudflare?
- Directory
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- © 2026 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark
-