Screen Share - Flutter
Screen sharing in a meeting is the process of sharing your device screen with other participants in the meeting. It allows everyone in the meeting to see exactly what you are seeing on your screen, which can be helpful for presentations, demonstrations, or collaborations.
Screen Share feature Compatibility
Android and iOS app | Web | Desktop app | Safari browser |
---|---|---|---|
enableScreenShare()
-
By using
enableScreenShare()
function ofRoom
object, local participant can share his/her screen or window to other participants. -
Screen Share stream of the participant can be accessed from the
streams
property ofParticipant
object. -
To enable Screenshare in Android devices, you need to provide the following permissions in
AndroidManifest.xml
.
System Audio with Screen Share (Android Only)
-
System Audio Sharing during screen sharing, allowing users to share both the screen and the audio from their device simultaneously.
-
Bool enableAudio
: In theenableScreenShare()
method, the second parameter,enableAudio
, is a flag that controls whether system audio is shared. Set it totrue
to include system audio with the screen share, orfalse
to share only the screen without audio. -
Ensure that you request the necessary permissions (
android.permission.RECORD_AUDIO
) for screen sharing and system audio capture on the user's device. The required permissions may vary based on the Android version and device manufacturer. -
Devices running
Android 9
(API Level 28
) or below will not support the System Audio sharing feature.
System audio will only be shared if the microphone is enabled. If the microphone is muted, the system audio will also be muted.
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
//For Android devices versioned 14 and above.
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
disableScreenShare()
- By using
disableScreenShare()
function ofRoom
object, local participant can stop sharing his/her screen or window to other participants.
import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';
class MeetingScreen extends StatefulWidget {
...
}
class _MeetingScreenState extends State<MeetingScreen> {
late Room _room;
@override
void initState() {
...
}
@override
Widget build(BuildContext context) {
return Column(
children:[
ElevatedButton(
onPressed:(){
if(!kIsWeb && Platform.isAndroid){
_room.enableScreenShare(enableAudio: true); // Enable screen share with audio on Android
}else{
_room.enableScreenShare(); // Enable screen share without audio on other platforms
}
},
child: const Text("Enable Screen Share"),
),
ElevatedButton(
onPressed:(){
_room.disableScreenShare(); // Disable screen share
},
child: const Text("Disable Screen Share"),
),
]
);
}
}
To ensure that the microphone and camera work in the background, you must implement background modes
iOS Setup
For Flutter iOS Screen Share feature, you need to follow this guide Flutter iOS Screen Share
Rendering Screen Share Stream
- To render the screenshare, you will need the
participantId
who is presenting the screen, which can be found from thepresenterId
property ofRoom
object.
We will listen for the Events.presenterChanged
on Room
object to check if some other participant starts screen share, Events.streamEnabled
and Events.streamDisabled
on the localParticipant
's object to identify if the local participant is presenting or not.
import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';
import './screen_share_view.dart';
class MeetingScreen extends StatefulWidget {
final String meetingId;
final String token;
const MeetingScreen(
{super.key, required this.meetingId, required this.token});
@override
State<MeetingScreen> createState() => _MeetingScreenState();
}
class _MeetingScreenState extends State<MeetingScreen> {
late Room _room;
String? _presenterId;
@override
void initState() {
// create room
_room = VideoSDK.createRoom(
roomId: widget.meetingId,
token: widget.token,
displayName: "John Doe",
micEnabled: true,
camEnabled: true,
defaultCameraIndex:
1, // Index of MediaDevices will be used to set default camera
);
setMeetingEventListener();
// Join room
_room.join();
super.initState();
}
// listening to meeting events
void setMeetingEventListener() {
_room.on(Events.roomLeft, () {
participants.clear();
Navigator.popUntil(context, ModalRoute.withName('/'));
});
//Listening if remote participant starts presenting
_room.on(Events.presenterChanged, (String? presenterId) {
setState(() => {_presenterId = presenterId});
});
//Listening if local participant starts presenting
_room.localParticipant.on(Events.streamEnabled, (Stream stream) {
if (stream.kind == "share") {
setState(() => {_presenterId = _room.localParticipant.id});
}
});
_room.localParticipant.on(Events.streamDisabled, (Stream stream) {
if (stream.kind == "share") {
setState(() => {_presenterId = null});
}
});
}
// onbackButton pressed leave the room
Future<bool> _onWillPop() async {
_room.leave();
return true;
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _onWillPop(),
child: Scaffold(
appBar: AppBar(
title: const Text('VideoSDK QuickStart'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text(widget.meetingId),
//we will render the screenshare view if the presenterId is not null
if (_presenterId != null)
ScreenShareView(
participant: _presenterId == _room.localParticipant.id
? _room.localParticipant
: _room.participants[_presenterId],
),
ElevatedButton(
onPressed: () {
if (_presenterId != null) {
_room.disableScreenShare();
} else {
if(!kIsWeb && Platform.isAndroid){
_room.enableScreenShare(enableAudio: true); // Enable screen share with audio on Android
}else{
_room.enableScreenShare(); // Enable screen share without audio on other platforms
}
}
},
child: const Text('Toggle Screnshare')),
],
),
),
),
);
}
}
- Now that we know if there is an active presenter, let us get the screen share stream from the
Participant
object and render it.
import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';
class ScreenShareView extends StatelessWidget {
final Participant? participant;
ScreenShareView({super.key, required this.participant}) {
//intialize the shareStream
participant?.streams.forEach((key, value) {
if (value.kind == "share") {
shareStream = value;
}
});
}
Stream? shareStream;
@override
Widget build(BuildContext context) {
return Container(
color: Colors.grey.shade800,
height: 300,
width: 200,
//show the screen share stream
child: shareStream != null
? RTCVideoView(
shareStream?.renderer as RTCVideoRenderer,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
)
: null,
);
}
}
Events associated with enableScreenShare
-
Every Participant will receive a callback on
Events.streamEnabled
of theParticipant
object withStream
object. -
Every Remote Participant will receive
Events.presenterChanged
callback on theRoom
object with the participantId aspresenterId
who started the screen share.
Events associated with disableScreenShare
-
Every Participant will receive a callback on
Events.streamDisabled
of theParticipant
object withStream
object. -
Every Remote Participant will receive
Events.presenterChanged
callback on theRoom
object with thepresenterId
asnull
.
import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';
class ParticipantTile extends StatefulWidget {
final Participant participant;
...
}
class _ParticipantTileState extends State<ParticipantTile> {
@override
void initState() {
...
_initStreamListeners();
super.initState();
}
//Change state according to the events received
_initStreamListeners() {
widget.participant.on(Events.streamEnabled, (Stream stream) {
if (stream.kind == 'share') {
//Screen share Turned On
}
});
widget.participant.on(Events.streamDisabled, (Stream stream) {
if (stream.kind == 'share') {
//Screen Share Turned Off
}
});
}
@override
Widget build(BuildContext context) {
return YourParticipantWidget();
}
}
class MeetingScreen extends StatefulWidget {
...
}
class _MeetingScreenState extends State<MeetingScreen> {
late Room room;
@override
void initState() {
...
setupRoomEventListener();
}
@override
Widget build(BuildContext context) {
return YourMeetingWidget();
}
void setupRoomEventListener() {
room.on(Events.presenterChanged, (String? presenterId) {
//Room screen presenter has changed
//Participant ID of current presenter is presenterId
});
}
}
API Reference
The API references for all the methods and events utilised in this guide are provided below.
Got a Question? Ask us on discord