startShareScreen function
Starts the screen sharing process.
Supports the following platforms:
- Web: Uses browser's getDisplayMedia API
- Android: Uses MediaProjection API (user will see a permission dialog)
- Windows/macOS/Linux: Uses native screen capture APIs with source picker
- iOS: Not supported (requires Broadcast Upload Extension)
@param StartShareScreenOptions options - The options for starting screen sharing. @param StartShareScreenParameters options.parameters - The parameters for screen sharing.
This function displays an alert if screen sharing fails or is not supported.
It also calls the streamSuccessScreen function when sharing is successful.
Example:
final options = StartShareScreenOptions(
parameters: StartShareScreenParameters(
shared: false,
showAlert: (msg, type, duration) => print(msg),
updateShared: (isShared) => print("Shared: $isShared"),
onWeb: true,
streamSuccessScreen: (stream, parameters) async => print("Success"),
),
targetWidth: 1920,
targetHeight: 1080,
);
await startShareScreen(options);
Implementation
Future<void> startShareScreen(StartShareScreenOptions options) async {
final targetWidth = options.targetWidth ?? 1280;
final targetHeight = options.targetHeight ?? 720;
final parameters = options.parameters;
bool shared = parameters.shared;
final showAlert = parameters.showAlert;
final updateShared = parameters.updateShared;
final streamSuccessScreen = parameters.streamSuccessScreen;
try {
// Check if screen sharing is supported on this platform
if (!_isScreenShareSupported()) {
if (showAlert != null) {
showAlert(
message: _getUnsupportedMessage(),
type: 'danger',
duration: 3000,
);
}
return;
}
// Platform-specific preparations BEFORE requesting screen capture
if (Platform.isAndroid) {
// Android requires a foreground service to be running during screen capture.
// This is REQUIRED on Android 10+ (API 29) and MANDATORY on Android 14+ (API 34)
// Without this, the app will crash when trying to start MediaProjection
await _startAndroidForegroundService();
}
// iOS: flutter_webrtc handles ReplayKit via getDisplayMedia when
// RTCAppGroupIdentifier + RTCScreenSharingExtension are set in Info.plist.
// The system broadcast picker appears automatically.
// Request screen capture using getDisplayMedia
// This works on Web, Android, iOS, Windows, macOS, and Linux
try {
MediaStream stream;
// Desktop platforms need to use desktopCapturer to select a source
if (_needsDesktopCapturer()) {
final context = options.context;
if (context == null) {
// No context provided - try to get sources directly and use first screen
final sources = await desktopCapturer.getSources(
types: [SourceType.Screen],
);
if (sources.isEmpty) {
await _stopAndroidForegroundService();
throw Exception('No screens available to share');
}
// Use the first screen
stream = await navigator.mediaDevices.getDisplayMedia({
'video': {
'deviceId': {'exact': sources.first.id},
'width': targetWidth,
'height': targetHeight,
'frameRate': 30
},
'audio': false
});
} else {
// Show screen picker dialog
// ignore: use_build_context_synchronously
final selectedSource = await _showScreenPickerDialog(context);
if (selectedSource == null) {
// User cancelled
await _stopAndroidForegroundService();
updateShared(false);
return;
}
stream = await navigator.mediaDevices.getDisplayMedia({
'video': {
'deviceId': {'exact': selectedSource.id},
'width': targetWidth,
'height': targetHeight,
'frameRate': 30
},
'audio': false
});
}
} else if (kIsWeb) {
// Web uses standard getDisplayMedia
stream = await navigator.mediaDevices.getDisplayMedia({
'video': {
'cursor': 'always',
'width': targetWidth,
'height': targetHeight,
'frameRate': 30
},
'audio': false
});
} else if (Platform.isIOS) {
// iOS uses ReplayKit via flutter_webrtc's native getDisplayMedia.
// flutter_webrtc reads RTCAppGroupIdentifier & RTCScreenSharingExtension
// from Info.plist and shows the system broadcast picker.
// The BroadcastExtension captures frames and sends them over a Unix socket.
//
// IMPORTANT: deviceId MUST start with "broadcast" to trigger
// FlutterBroadcastScreenCapturer instead of FlutterRPScreenRecorder.
// Without this, it uses in-app ReplayKit which causes "hall of mirrors".
stream = await navigator.mediaDevices.getDisplayMedia({
'video': {
'deviceId': 'broadcast',
},
'audio': false,
});
} else {
// Android and other platforms use standard getDisplayMedia
// flutter_webrtc 1.2.1+ supports these natively
stream = await navigator.mediaDevices.getDisplayMedia({
'video': {
'cursor': 'always',
'width': targetWidth,
'height': targetHeight,
'frameRate': 30
},
'audio': false
});
}
// Produce the track for all platforms
try {
final optionsStream = StreamSuccessScreenOptions(
stream: stream,
parameters: parameters,
);
await streamSuccessScreen(optionsStream);
} catch (e) {
rethrow;
}
shared = true;
} catch (error) {
shared = false;
// Stop foreground service on error
await _stopAndroidForegroundService();
String errorMessage = 'Could not share screen, check and retry';
// Provide more specific error messages
if (error.toString().contains('NotAllowedError') ||
error.toString().contains('Permission')) {
errorMessage =
'Screen sharing permission denied. Please allow screen capture and try again.';
} else if (error.toString().contains('NotFoundError') ||
error.toString().contains('source not found')) {
errorMessage = 'No screen found to share.';
} else if (error.toString().contains('NotSupportedError')) {
errorMessage = 'Screen sharing is not supported on this device.';
} else if (error.toString().contains('cancelled') ||
error.toString().contains('canceled')) {
// User cancelled - not an error
updateShared(false);
return;
}
showAlert?.call(
message: errorMessage,
type: 'danger',
duration: 3000,
);
}
// Update the shared variable
updateShared(shared);
} catch (error) {
rethrow;
}
}