DEV Community

Kishan Nakrani for Video SDK

Posted on • Originally published at videosdk.live on

How to Implement Screen Share in Flutter Video Call App for iOS?

πŸ“Œ Introduction

How to Implement Screen Share in Flutter Video Call App for iOS?

screen sharing functionality into your Flutter video call app for iOS to enhance user experience and collaboration. With screen sharing, users can seamlessly share their screen content during video calls, fostering effective communication and teamwork. Utilizing Flutter's versatile framework, integrate a user-friendly interface that allows users to initiate screen sharing with just a few taps. VideoSDK offers easy-to-use APIs and comprehensive documentation, simplifying the integration process for developers.

Benefits of Implementing Screen Share in Flutter iOS Video Call App:

  1. Enhanced Collaboration: Screen sharing facilitates real-time sharing of information, fostering better collaboration among users.
  2. Improved Communication: Visual demonstrations and presentations help convey complex ideas more effectively, leading to clearer communication.
  3. Increased Productivity: With the ability to share screens, teams can work together more efficiently, reducing misunderstandings and saving time.
  4. Interactive Learning: Educators and trainers can use screen sharing to demonstrate concepts, conduct workshops, and engage learners interactively.
  5. Client Presentations: Professionals can showcase designs, reports, or demos directly to clients, enhancing engagement and understanding.

Use cases of Screen Share in Flutter iOS Video Call App:

  1. Remote Work: Teams working remotely can collaborate effectively by sharing screens during meetings, brainstorming sessions, and code reviews.
  2. Education: Teachers can use screen sharing to present slideshows, demonstrate software, or conduct virtual experiments for remote students.
  3. Technical Support: IT professionals can troubleshoot software issues by remotely accessing and viewing users' screens to identify and resolve problems.

In this article, we'll explore how to integrate screen sharing into your Flutter video calling app for iOS using VideoSDK. We'll cover everything from managing permissions to leveraging VideoSDK's tools and seamlessly integrating the feature into your app's interface.

πŸš€ Getting Started with VideoSDK

To take advantage of Screen Share for iOS, we must use the capabilities that the VideoSDK offers. Before diving into the implementation steps, let's ensure you complete the necessary prerequisites.

Create a VideoSDK Account

Go to your VideoSDK dashboard and sign up if you don't have an account. This account gives you access to the required Video SDK token, which acts as an authentication key that allows your application to interact with VideoSDK functionality.

Generate your Auth Token

Visit your VideoSDK dashboard and navigate to the "API Key" section to generate your auth token. This token is crucial in authorizing your application to use VideoSDK features.

For a more visual understanding of the account creation and token generation process, consider referring to the provided tutorial.

Prerequisites​

Before proceeding, ensure that your development environment meets the following requirements:

πŸ› οΈ Install VideoSDK​

Install the VideoSDK using the below-mentioned flutter command. Make sure you are in your Flutter app directory before you run this command.

$ flutter pub add videosdk

//run this command to add http library to perform network call to generate roomId
$ flutter pub add http
Enter fullscreen mode Exit fullscreen mode

VideoSDK Compatibility

Android and iOS app Web Desktop app Safari browser
βœ…
βœ…
βœ…
❌

Structure of the project​

Your project structure should look like this.

    root
    β”œβ”€β”€ android
    β”œβ”€β”€ ios
    β”œβ”€β”€ lib
         β”œβ”€β”€ api_call.dart
         β”œβ”€β”€ join_screen.dart
         β”œβ”€β”€ main.dart
         β”œβ”€β”€ meeting_controls.dart
         β”œβ”€β”€ meeting_screen.dart
         β”œβ”€β”€ participant_tile.dart
Enter fullscreen mode Exit fullscreen mode

We are going to create flutter widgets (JoinScreen, MeetingScreen, MeetingControls, and ParticipantTile).

App Structure​

The app widget will contain JoinScreen and MeetingScreen widget. MeetingScreen will have MeetingControls and ParticipantTile widget.

How to Implement Screen Share in Flutter Video Call App for iOS?

Configure Project

For iOS​

  • Add the following entries that allow your app to access the camera and microphone in your /ios/Runner/Info.plist file:
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) Camera Usage!</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) Microphone Usage!</string>
Enter fullscreen mode Exit fullscreen mode
  • Uncomment the following line to define a global platform for your project in /ios/Podfile :
# platform :ios, '12.0'
Enter fullscreen mode Exit fullscreen mode

πŸŽ₯ Essential Steps to Implement Video Calling Functionality

Before diving into the specifics of screen-sharing implementation, it's crucial to ensure you have VideoSDK properly installed and configured within your Flutter project. Refer to VideoSDK's documentation for detailed installation instructions. Once you have a functional video calling setup, you can proceed with adding the screen-sharing feature.

Step 1: Get started with api_call.dart​

Before jumping to anything else, you will write a function to generate a unique meetingId. You will require an authentication token, you can generate it either by using videosdk-rtc-api-server-examples or by generating it from the VideoSDK Dashboard for development.

import 'dart:convert';
import 'package:http/http.dart' as http;

//Auth token we will use to generate a meeting and connect to it
String token = "<Generated-from-dashboard>";

// API call to create meeting
Future<String> createMeeting() async {
  final http.Response httpResponse = await http.post(
    Uri.parse("https://api.videosdk.live/v2/rooms"),
    headers: {'Authorization': token},
  );

//Destructuring the roomId from the response
  return json.decode(httpResponse.body)['roomId'];
}
Enter fullscreen mode Exit fullscreen mode

api_call.dart

Step 2: Creating the JoinScreen​

Let's create join_screen.dart file in lib directory and create JoinScreen StatelessWidget.

The JoinScreen will consist of:

  • Create Meeting Button : This button will create a new meeting for you.
  • Meeting ID TextField : This text field will contain the meeting ID, you want to join.
  • Join Meeting Button : This button will join the meeting, which you have provided.
import 'package:flutter/material.dart';
import 'api_call.dart';
import 'meeting_screen.dart';

class JoinScreen extends StatelessWidget {
  final _meetingIdController = TextEditingController();

  JoinScreen({super.key});

  void onCreateButtonPressed(BuildContext context) async {
    // call api to create meeting and then navigate to MeetingScreen with meetingId,token
    await createMeeting().then((meetingId) {
      if (!context.mounted) return;
      Navigator.of(context).push(
        MaterialPageRoute(
          builder: (context) => MeetingScreen(
            meetingId: meetingId,
            token: token,
          ),
        ),
      );
    });
  }

  void onJoinButtonPressed(BuildContext context) {
    String meetingId = _meetingIdController.text;
    var re = RegExp("\\w{4}\\-\\w{4}\\-\\w{4}");
    // check meeting id is not null or invaild
    // if meeting id is vaild then navigate to MeetingScreen with meetingId,token
    if (meetingId.isNotEmpty && re.hasMatch(meetingId)) {
      _meetingIdController.clear();
      Navigator.of(context).push(
        MaterialPageRoute(
          builder: (context) => MeetingScreen(
            meetingId: meetingId,
            token: token,
          ),
        ),
      );
    } else {
      ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
        content: Text("Please enter valid meeting id"),
      ));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('VideoSDK QuickStart'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(12.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () => onCreateButtonPressed(context),
              child: const Text('Create Meeting'),
            ),
            Container(
              margin: const EdgeInsets.fromLTRB(0, 8.0, 0, 8.0),
              child: TextField(
                decoration: const InputDecoration(
                  hintText: 'Meeting Id',
                  border: OutlineInputBorder(),
                ),
                controller: _meetingIdController,
              ),
            ),
            ElevatedButton(
              onPressed: () => onJoinButtonPressed(context),
              child: const Text('Join Meeting'),
            ),
          ],
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

join_screen.dart

  • Update the home screen of the app in the main.dart
import 'package:flutter/material.dart';
import 'join_screen.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'VideoSDK QuickStart',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: JoinScreen(),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

main.dart

Step 3: Creating the MeetingControls​

Let's create meeting_controls.dart file and create MeetingControls StatelessWidget.

The MeetingControls will consist of:

  • Leave Button : This button will leave the meeting.
  • Toggle Mic Button : This button will unmute or mute the mic.
  • Toggle Camera Button : This button will enable or disable the camera.

MeetingControls will accept 3 functions in the constructor

  • onLeaveButtonPressed : invoked when the Leave button pressed
  • onToggleMicButtonPressed : invoked when the toggle mic button pressed
  • onToggleCameraButtonPressed : invoked when the toggle Camera button pressed
import 'package:flutter/material.dart';

class MeetingControls extends StatelessWidget {
  final void Function() onToggleMicButtonPressed;
  final void Function() onToggleCameraButtonPressed;
  final void Function() onLeaveButtonPressed;

  const MeetingControls(
      {super.key,
      required this.onToggleMicButtonPressed,
      required this.onToggleCameraButtonPressed,
      required this.onLeaveButtonPressed});

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        ElevatedButton(
            onPressed: onLeaveButtonPressed, child: const Text('Leave')),
        ElevatedButton(
            onPressed: onToggleMicButtonPressed, child: const Text('Toggle Mic')),
        ElevatedButton(
            onPressed: onToggleCameraButtonPressed,
            child: const Text('Toggle WebCam')),
      ],
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

meeting_controls.dart

Step 4: Creating ParticipantTile​

Let's create participant_tile.dart file and create ParticipantTile StatefulWidget.

The ParticipantTile will consist of:

  • RTCVideoView : This will show the participant's video stream.

ParticipantTile will accept Participant in constructor

  • participant: participant of the meeting.
import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';

class ParticipantTile extends StatefulWidget {
  final Participant participant;
  const ParticipantTile({super.key, required this.participant});

  @override
  State<ParticipantTile> createState() => _ParticipantTileState();
}

class _ParticipantTileState extends State<ParticipantTile> {
  Stream? videoStream;

  @override
  void initState() {
    // initial video stream for the participant
    widget.participant.streams.forEach((key, Stream stream) {
      setState(() {
        if (stream.kind == 'video') {
          videoStream = stream;
        }
      });
    });
    _initStreamListeners();
    super.initState();
  }

  _initStreamListeners() {
    widget.participant.on(Events.streamEnabled, (Stream stream) {
      if (stream.kind == 'video') {
        setState(() => videoStream = stream);
      }
    });

    widget.participant.on(Events.streamDisabled, (Stream stream) {
      if (stream.kind == 'video') {
        setState(() => videoStream = null);
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: videoStream != null
          ? RTCVideoView(
              videoStream?.renderer as RTCVideoRenderer,
              objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
            )
          : Container(
              color: Colors.grey.shade800,
              child: const Center(
                child: Icon(
                  Icons.person,
                  size: 100,
                ),
              ),
            ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

participant_tile.dart

Step 5: Creating the MeetingScreen​

Let's create meeting_screen.dart file and create MeetingScreen StatefulWidget.

MeetingScreen will accept meetingId and token in the constructor

  • meetingId: meetingId, you want to join
  • token : VideoSdk Auth token

Your app should look like this after the implementation.

CAUTION

If you get webrtc/webrtc.h file not found error at runtime in ios then check the solution here.

TIP

You can check out the complete quick start example here.

πŸ”„ Integrate Screen Share Feature

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.

Why Integrate Screen Sharing with VideoSDK?

Integrating screen sharing into your Flutter video calling app using videoSDK presents several compelling reasons:

  1. Simplified Development : With VideoSDK, the intricate tasks of screen capture and transmission are effortlessly managed, saving you valuable development time and resources.
  2. Native Functionality : Harnessing the power of the iOS Media Projection API, VideoSDK ensures a seamless and efficient screen-sharing experience for your users, providing them with a native feel and optimal performance.
  3. Seamless Integration : The integration process with VideoSDK is intuitive and hassle-free, enabling you to seamlessly incorporate screen-sharing functionality into your existing video call features without extensive modifications.
  4. Robust Features : VideoSDK offers a robust screen-sharing solution equipped with essential features such as permission handling and in-app sharing capabilities, guaranteeing a reliable and user-friendly experience.

By embracing screen sharing through VideoSDK, you elevate the functionality and appeal of your Flutter video-calling app, providing users with an enriched communication experience.

NOTE:

Flutter iOS screenshare is available from the SDK version 1.0.5 and above and the deployment target for iOS app should be iOS 14 or newer

iOS requires you to add a Broadcast Upload Extension to capture the screen of the device.

Step 1: Open Target​

Open your project with Xcode and select File > New > Target in the menu bar.

How to Implement Screen Share in Flutter Video Call App for iOS?

Step 2: Select Target​

Choose Broadcast Upload Extension , and click next.

How to Implement Screen Share in Flutter Video Call App for iOS?

Step 3: Configure Broadcast Upload Extension​

Enter the extension name in the Product Name field, choose a team from the dropdown, Uncheck the include UI extension field, and click finish.

How to Implement Screen Share in Flutter Video Call App for iOS?

Step 4: Activate Extension scheme​

You will be prompted to Activate the "Your-Extension-name" scheme? pop-up, click activate.

How to Implement Screen Share in Flutter Video Call App for iOS?

Now, the broadcast folder will appear in Xcode left sidebar.

How to Implement Screen Share in Flutter Video Call App for iOS?

Step 5: Add External file in Created Extension​

Open videosdk-rtc-flutter-sdk-example, Copy SampleUploader.swift, SocketConnection.swift, DarwinNotificationCenter.swift, and Atomic.swift files to your extension's folder and make sure they're added to the target.

Step 6: Update SampleHandler.swift file​

Open SampleHandler.swift and Copy SampleHandler.swift file content and paste it into your extensions SampleHandler.swift file.

Step 7: Add Capability in the App​

In Xcode, go to YourappName > Signing & Capabilities. and click on +Capability to configure the app group.

How to Implement Screen Share in Flutter Video Call App for iOS?

Select App Groups from the list:

How to Implement Screen Share in Flutter Video Call App for iOS?

After that, you have to select or add the generated app group ID that you have created before.

How to Implement Screen Share in Flutter Video Call App for iOS?

Step 8: Add Capability in Extension​

Go to Your-Extension-Name > Signing & Capabilities and configure App Group functionality, which we had performed in previous steps. (The group ID should be the same for both targets.)

How to Implement Screen Share in Flutter Video Call App for iOS?

Step 9: Add App Group ID to Extension File​

Go to the extensions SampleHandler.swift file and paste your group Id in appGroupIdentifier constant.

How to Implement Screen Share in Flutter Video Call App for iOS?

Step 10: Update App level info.plist file​

  1. Add a new key, RTCScreenSharingExtension in Info.plist with the extension's Bundle Identifier as the value.
  2. Add a new key, RTCAppGroupIdentifier in Info.plist with the extension's app group ID as the value.

Note : For extension's Bundle Identifier, go to TARGETS > Your-Extension-Name > Signing & Capabilities.

How to Implement Screen Share in Flutter Video Call App for iOS?

NOTE: You can also check out the extension example code on GitHub.

[

videosdk-rtc-flutter-sdk-example/ios/FlutterBroadcast at main Β· videosdk-live/videosdk-rtc-flutter-sdk-example

WebRTC based video conferencing SDK for Flutter (Android / iOS) - videosdk-live/videosdk-rtc-flutter-sdk-example

How to Implement Screen Share in Flutter Video Call App for iOS?GitHubvideosdk-live

How to Implement Screen Share in Flutter Video Call App for iOS?
](https://github.com/videosdk-live/videosdk-rtc-flutter-sdk-example/tree/main/ios/FlutterBroadcast)

Enable Screen Share​

  • To start screen sharing, just call room.enableScreenShare() method.
ElevatedButton(
    child: Text("Start ScreenSharing"),
    onPressed: () {
        room.enableScreenShare();
    },
),
Enter fullscreen mode Exit fullscreen mode

How to Implement Screen Share in Flutter Video Call App for iOS?

After clicking the Start Broadcast button, we will be able to get the screen stream in session.

Disable Screen Share​

By using room.disableScreenShare() function, a participant can stop publishing screen streams to other participants

ElevatedButton(
    child: Text("Stop ScreenSharing"),
    onPressed: () {
        room.disableScreenShare();
    },
),
Enter fullscreen mode Exit fullscreen mode

Events associated with Screen Share​

Events associated with Enable ScreenShare​

Every Participant will receive a callback on Events.streamEnabled of the Participant object with Stream object.

  • Every Remote Participant will receive Events.presenterChanged callback on the Room object with the participantId as presenterId who started the screen share. <!--kg-card-begin: markdown--> #### Events associated with Disable ScreenShare​ <!--kg-card-end: markdown-->
  • Every Participant will receive a callback on Events.streamDisabled of the Participant object with Stream object.
  • Every Remote Participant will receive Events.presenterChanged callback on the Room object with the presenterId as null.

Example​

To render the screen share, you will need to know participantId who is presenting the screen, which can be found in the presenterId property of Room 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/foundation.dart';
import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';
import './participant_tile.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;
  var micEnabled = true;
  var camEnabled = true;
  String? _presenterId;

  Map<String, Participant> participants = {};

  @override
  void initState() {
    // create room
    _room = VideoSDK.createRoom(
      roomId: widget.meetingId,
      token: widget.token,
      displayName: "John Doe",
      micEnabled: micEnabled,
      camEnabled: camEnabled
    );

    setMeetingEventListener();

    // Join room
    _room.join();

    super.initState();
  }

  // listening to meeting events
  void setMeetingEventListener() {
   // ...

   //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;
  }

  // This widget is the root of your application.
  @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),
              //render all participant
              // ...

              //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],
                ),
                // MeetingControls
                // ....
              ElevatedButton(
                  onPressed: () {
                    if (_presenterId != null) {
                      _room.disableScreenShare();
                    } else {
                      _room.enableScreenShare();
                    }
                  },
                  child: const Text('Toggle Screnshare')),
            ],
          ),
        ),
      ),
      home: JoinScreen(),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

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,
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”š Conclusion

By integrating screen sharing into your Flutter video calling app using VideoSDK, you provide users with a powerful tool to enhance communication and collaboration. This feature proves beneficial across various scenarios, from business presentations to technical demonstrations, making your app a versatile and invaluable platform. With VideoSDK'sseamless integration capabilities, incorporating screen sharing becomes not only feasible but also streamlined. This allows you to focus more on crafting a remarkable user interface and experience. Whether users need to share slides, collaborate on projects, or provide remote assistance, your app becomes their go-to solution. Empower users with the ability to connect, communicate, and collaborate effectively, all within a single, user-friendly platform.

Unlock the full potential of VideoSDK and create seamless video experiences today! Sign up now and get 10,000 free minutes to elevate your video app to the next level.

Top comments (0)