no_screen_mirror

A Flutter plugin to detect screen mirroring (AirPlay, Miracast), external display connections (HDMI, USB-C, DisplayPort, VGA, DVI), and screen sharing in video calls.

Features

  • Detect screen mirroring (AirPlay on iOS/macOS, Miracast on Android/Windows)
  • Detect external display connections (HDMI, USB-C, DisplayPort, VGA, DVI)
  • Detect screen sharing and recording (Zoom, Teams, Slack, Discord, OBS, etc.)
  • Report the total number of connected displays
  • Real-time streaming of display state changes
  • Configurable polling interval for battery-sensitive apps
  • Custom screen sharing process names for extensible detection
  • Platform capability reporting at runtime
  • Cross-platform: Android, iOS, macOS, Linux, Windows, and Web

Platform Support

Platform Mirroring External Display Screen Sharing Detection Method
Android Yes (Miracast) Yes Yes (API 34+) DisplayManager + MediaRouter + ScreenCaptureCallback
iOS Yes (AirPlay) Yes Yes (iOS 11+) UIScreen notifications + isCaptured
macOS Yes Yes Yes CoreGraphics + process detection
Linux No Yes Yes /sys/class/drm + /proc scanning
Windows Yes (Miracast) Yes Yes Win32 Display Config + process scanning
Web No Chromium 100+ No Screen.isExtended API

Installation

Add no_screen_mirror to your pubspec.yaml:

dependencies:
  no_screen_mirror: ^0.1.0

Then run:

flutter pub get

Permissions

No special permissions are required on any platform. The plugin uses only standard system APIs that are available without additional entitlements or manifest declarations.

Usage

Basic Example

import 'package:no_screen_mirror/no_screen_mirror.dart';
import 'package:no_screen_mirror/mirror_snapshot.dart';

final plugin = NoScreenMirror.instance;

// Start listening for display changes
await plugin.startListening();

// Listen to the mirror stream
plugin.mirrorStream.listen((MirrorSnapshot snapshot) {
  print('Screen mirrored: ${snapshot.isScreenMirrored}');
  print('External display: ${snapshot.isExternalDisplayConnected}');
  print('Screen shared: ${snapshot.isScreenShared}');
  print('Display count: ${snapshot.displayCount}');
});

// Stop listening when done
await plugin.stopListening();

Configurable Polling Interval

Control how often the plugin scans for changes on platforms that use polling (Linux, Windows, macOS, Web). iOS and Android are event-driven and ignore this setting.

await plugin.startListening(
  pollingInterval: const Duration(seconds: 5), // Default: 2 seconds
);

Custom Screen Sharing Process Names

Add your own process names or bundle IDs to the detection list. This extends (does not replace) the built-in list.

await plugin.startListening(
  customScreenSharingProcesses: [
    'my-conferencing-app', // Linux process name
    'com.example.myapp',   // macOS bundle ID
    'MyApp.exe',           // Windows process name
  ],
);

Platform Capabilities

Check at runtime what the current platform can detect:

import 'package:no_screen_mirror/mirror_capabilities.dart';

final caps = NoScreenMirror.platformCapabilities;

if (caps.canDetectScreenSharing) {
  // Enable screen sharing UI
}

print(caps.notes); // Platform-specific notes

With StreamSubscription

import 'dart:async';
import 'package:no_screen_mirror/no_screen_mirror.dart';
import 'package:no_screen_mirror/mirror_snapshot.dart';

class _MyWidgetState extends State<MyWidget> {
  final _plugin = NoScreenMirror.instance;
  StreamSubscription<MirrorSnapshot>? _subscription;
  MirrorSnapshot? _snapshot;

  Future<void> _startListening() async {
    await _plugin.startListening();
    _subscription = _plugin.mirrorStream.listen((snapshot) {
      if (!mounted) return;
      setState(() => _snapshot = snapshot);
    });
  }

  Future<void> _stopListening() async {
    await _plugin.stopListening();
    _subscription?.cancel();
    _subscription = null;
  }

  @override
  void dispose() {
    _subscription?.cancel();
    super.dispose();
  }
}

API Reference

NoScreenMirror

Member Type Description
instance NoScreenMirror Singleton accessor
platformCapabilities MirrorCapabilities Runtime platform capability info (static)
mirrorStream Stream<MirrorSnapshot> Stream of display state updates
startListening() Future<void> Begin monitoring for display changes
stopListening() Future<void> Stop monitoring

startListening Parameters

Parameter Type Default Description
pollingInterval Duration Duration(seconds: 2) How often to scan on polling-based platforms
customScreenSharingProcesses List<String> [] Additional process names to detect as screen sharing

MirrorSnapshot

Property Type Description
isScreenMirrored bool Whether the screen is being mirrored (AirPlay/Miracast)
isExternalDisplayConnected bool Whether an external display is connected (HDMI, USB-C, etc.)
isScreenShared bool Whether screen sharing or recording is active
displayCount int Total number of connected displays

MirrorCapabilities

Property Type Description
canDetectMirroring bool Whether mirroring detection is supported
canDetectExternalDisplay bool Whether external display detection is supported
canDetectScreenSharing bool Whether screen sharing detection is supported
platform String Current platform identifier
notes String Platform-specific limitations and notes

Platform Notes

Android

Requires Android 4.2 (API 17) or later. Uses DisplayManager for external display detection and MediaRouter for Miracast/wireless mirroring detection. Screen sharing detection uses Activity.ScreenCaptureCallback which requires Android 14+ (API 34) — on older versions, isScreenShared is always false.

iOS

Uses UIScreen notifications for display connection events and isCaptured (iOS 11+) for AirPlay mirroring and screen sharing detection. The isCaptured property covers screen recording, AirPlay mirroring, and screen sharing by third-party apps.

macOS

Uses CoreGraphics APIs (CGDisplayIsBuiltin, CGDisplayMirrorsDisplay) to distinguish external displays and detect mirroring. Screen sharing is detected by checking running application bundle IDs (Zoom, Teams, Slack, Discord, OBS, QuickTime, Loom). Custom bundle IDs can be added via customScreenSharingProcesses.

Linux

Scans /sys/class/drm/ for display connectors. Supports eDP, LVDS, DSI (built-in) and HDMI, DP, VGA, DVI (external). Screen mirroring detection is not available (always returns false) — there is no kernel-level mirroring API. Screen sharing is detected by scanning /proc/*/comm for known process names (zoom, teams, slack, discord, obs, ffmpeg, etc.).

Windows

Uses Win32 Display Configuration APIs for external display and Miracast detection via QueryDisplayConfig. Screen sharing is detected by scanning running processes via CreateToolhelp32Snapshot for known executables (Zoom.exe, Teams.exe, slack.exe, Discord.exe, obs64.exe, ffmpeg.exe, etc.).

Web

Uses the Screen.isExtended API available in Chromium 100+. Safari and Firefox are not supported (values default to false). Screen mirroring and screen sharing detection are not available in browsers. The plugin also listens for visibilitychange events to re-scan when the tab is shown/hidden.

Built-in Screen Sharing Process Lists

macOS (Bundle IDs)

us.zoom.xos, com.microsoft.teams, com.microsoft.teams2, com.tinyspeck.slackmacgap, com.hnc.Discord, com.obsproject.obs-studio, com.apple.QuickTimePlayerX, com.loom.desktop

Linux (Process Names)

zoom, teams, teams-for-linux, slack, discord, obs, ffmpeg, simplescreenrecorder, kazam, peek, recordmydesktop, vokoscreen

Windows (Executable Names)

Zoom.exe, CptHost.exe, Teams.exe, ms-teams.exe, slack.exe, Discord.exe, obs64.exe, obs32.exe, ffmpeg.exe