screen_share_plugin
A Flutter plugin that enables real-time screen sharing on Android using native Kotlin code and displays the stream via flutter_webrtc.
Features
- ✅ Real-time screen sharing on Android.
- ✅ Screen preview powered by
flutter_webrtc
. (Thanks to the flutter_webrtc team). - ✅ Native Android code written in Kotlin.
- ✅ Tested with Flutter 3.29 and compile SDK version up to 35 to reduce compatibility issues.
Getting Started
1. Installation
Add the plugin to your pubspec.yaml
:
dependencies:
screen_share_plugin: ^0.0.2
2. Android Setup
Ensure you are using:
- Flutter SDK:
3.29.0
- compileSdkVersion:
35
(in yourandroid/app/build.gradle
) - Allow Permissions from app info:
Notifications, Camera, Microphone for screen sharing
3. Permissions and Manifest Configuration (Optional)
Add the required permissions and service declaration to your AndroidManifest.xml
.
If your configuration doesn't work out of the box, try adding these manually:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Permissions required for screen sharing -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:label="screen_share_plugin_example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<!-- Foreground service declaration for media projection -->
<service
android:name="com.example.screen_share_plugin.ScreenCaptureService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="mediaProjection" />
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
Example Usage
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:screen_share_plugin/screen_share_plugin.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: ScreenShareDemo(),
debugShowCheckedModeBanner: false,
);
}
}
class ScreenShareDemo extends StatefulWidget {
const ScreenShareDemo({super.key});
@override
State<ScreenShareDemo> createState() => _ScreenShareDemoState();
}
class _ScreenShareDemoState extends State<ScreenShareDemo> {
bool _isSharing = false;
String _status = "Idle";
MediaStream? _localStream;
final RTCVideoRenderer _localRenderer = RTCVideoRenderer();
@override
void initState() {
super.initState();
_initializeRenderer();
}
Future<void> _initializeRenderer() async {
await _localRenderer.initialize();
}
Future<void> _startScreenSharing() async {
try {
setState(() {
_status = "Requesting permission...";
});
// Start the screen share plugin
await ScreenSharePlugin.startScreenShare();
// After starting the service, grab the display media
final mediaConstraints = <String, dynamic>{
'audio': false,
'video': {
'mandatory': {
'minWidth': 640,
'minHeight': 480,
'minFrameRate': 30,
},
'facingMode': 'user',
'optional': [],
}
};
_localStream =
await navigator.mediaDevices.getDisplayMedia(mediaConstraints);
_localRenderer.srcObject = _localStream;
setState(() {
_isSharing = true;
_status = "Screen sharing active";
});
} catch (e) {
setState(() {
_status = "Error: ${e.toString()}";
});
}
}
Future<void> _stopScreenSharing() async {
try {
await ScreenSharePlugin.stopScreenShare();
setState(() {
_isSharing = false;
_status = "Screen sharing stopped";
});
_localStream?.dispose();
_localRenderer.srcObject = null;
} catch (e) {
setState(() {
_status = "Error: ${e.toString()}";
});
}
}
@override
void dispose() {
_localStream?.dispose();
_localRenderer.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Screen Sharing Plugin Example"),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
_status,
style: const TextStyle(fontSize: 18),
),
const SizedBox(height: 30),
_isSharing
? SizedBox(
width: 640,
height: 480,
child: RTCVideoView(_localRenderer),
)
: const Center(child: Text("No Screen Sharing")),
const SizedBox(height: 30),
ElevatedButton.icon(
icon: Icon(_isSharing ? Icons.stop : Icons.screen_share),
label:
Text(_isSharing ? "Stop Sharing" : "Start Screen Sharing"),
onPressed:
_isSharing ? _stopScreenSharing : _startScreenSharing,
),
],
),
),
),
);
}
}
License
MIT License
Support
If you like this plugin, support me on Buy Me A Coffee.
If this plugin helped you, please consider subscribing to my YouTube channel 🎥 Yogx World