Skip to content

A Flutter plugin for iOS that uses ReplayKit to record the device screen and provides low-frequency preview frames (JPEG) for UI and analytics/computer vision. Designed for performance, clarity, and maintainability.

License

Notifications You must be signed in to change notification settings

fpinzn/ios_screen_recording_flutter_plugin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

iOS Screen Recording Flutter Plugin

A Flutter plugin for iOS that uses ReplayKit to record the device screen and provides low-frequency preview frames (JPEG) for UI and analytics/computer vision. Designed for performance, clarity, and maintainability.

  • iOS only (Swift 5)
  • Minimum iOS: 12.0
  • Target devices: iPhone 11 and newer recommended

Features

  • Start/stop screen capture (ReplayKit) from Flutter
  • Pull-based preview frames (JPEG) at up to a configured max FPS
  • Optional video recording (via a flag in startScreenCapture) to an .mp4 file (HEVC by default; H.264 fallback)
  • Optional save to Photos (camera roll) upon recording completion
  • Event stream for lifecycle and recording completion
  • Correct video timing and aspect ratio (writer starts at first sample PTS; output size respects source aspect)

Install

Add to your pubspec.yaml (for apps using this plugin):

dependencies:
  ios_screen_recording_flutter_plugin: ^0.0.1

iOS setup in your app:

  • Ensure minimum iOS 12 in your app target.
  • If you plan to save recordings to Photos, add to your app Info.plist:
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Allow saving screen recordings to your Photos.</string>

Quick start

final plugin = IosScreenRecordingFlutterPlugin();

// Start capture; set recordVideo to true to record while capturing
await plugin.startScreenCapture(
  previewConfig: const PreviewConfig(previewMaxFps: 5, previewMaxDimension: 320, jpegQuality: 0.5),
  recordVideo: true,
  recordingConfig: const RecordingConfig(
    videoCodec: 'hevc',           // 'hevc' | 'h264'
    videoResolution: '540p',      // '540p' | '720p' | 'screenNative'
    videoFps: 24,
    saveToCameraRoll: true,
  ),
);

// Pull latest frame at ~5 fps
final frame = await plugin.getLatestFrame(timeoutMs: 1200);
// frame.jpegBytes, frame.width, frame.height, frame.pixelRatio, frame.timestampMs

// Stop capture (and finalize recording if enabled)
await plugin.stopScreenCapture();

// Listen for events
plugin.events.listen((event) {
  if (event['type'] == 'screen_recording') {
    final data = Map<String, dynamic>.from(event['data'] as Map);
    final url = data['videoFileUrl'] as String?;
    final saved = data['savedToCameraRoll'] as bool?;
    final assetId = data['savedAssetLocalId'] as String?;
  }
});

API

Dart facade

  • Future<void> startScreenCapture({ PreviewConfig previewConfig, bool recordVideo = false, RecordingConfig? recordingConfig })
  • Future<void> stopScreenCapture()
  • Future<PreviewFrame?> getLatestFrame({ int timeoutMs = 1200 })
  • Stream<Map<String, dynamic>> get events

Types

class PreviewConfig {
  const PreviewConfig({
    this.previewMaxFps = 5,
    this.previewMaxDimension = 320,
    this.jpegQuality = 0.5,
  });
  final int previewMaxFps;         // 1..30
  final int previewMaxDimension;   // longest side in px (scaled image)
  final double jpegQuality;        // 0.1..0.95
}

class RecordingConfig {
  const RecordingConfig({
    this.videoCodec = 'hevc',         // 'hevc' | 'h264'
    this.videoResolution = '540p',    // '540p' | '720p' | 'screenNative'
    this.videoFps = 24,               // 10..60
    this.videoBitrateKbps,            // optional override
    this.saveToCameraRoll = false,    // save result to Photos when finished
  });
  final String videoCodec;
  final String videoResolution;
  final int videoFps;
  final int? videoBitrateKbps;
  final bool saveToCameraRoll;
}

class PreviewFrame {
  final Uint8List jpegBytes; // compressed JPEG preview
  final int width;           // scaled width in px
  final int height;          // scaled height in px
  final double pixelRatio;   // UIScreen scale at capture time
  final int timestampMs;     // ms epoch
}

Events

  • { type: 'started' } — capture started
  • { type: 'stopped' } — capture stopped
  • { type: 'screen_recording', data: { videoFileUrl, videoFileStartTimestamp, savedToCameraRoll?, savedAssetLocalId? } } — recording completed

Defaults and behavior

  • Preview frames (pull-only):
    • maxFps: 5
    • maxDimension: 320 px (longest side)
    • jpegQuality: 0.5
    • getLatestFrame(timeoutMs) returns the latest cached frame; if no frame yet, it waits up to max(1200ms, 1.5×frameInterval).
  • Recording:
    • codec: HEVC by default (fallback to H.264 if needed)
    • resolution: 540p by default; 720p and native screen size supported
    • fps: 24
    • bitrate: HEVC ~1.8 Mbps; H.264 ~3.0 Mbps (override via videoBitrateKbps)
    • audio: off
    • storage: app Documents dir (screen_<epoch>.mp4)
    • Photos save: set saveToCameraRoll: true (requires Info.plist key)
    • Video timing/aspect: writer session starts at first sample PTS; output size is computed from the first sample and target long-edge while preserving aspect ratio.

Example app

  • Shows preview frames at ~5 FPS.
  • Toggle to record video on start (and save to Photos).
  • Start/Stop buttons are disabled appropriately during capture:
    • Start disabled while capturing
    • Stop disabled when not capturing

Run on a physical iOS device:

cd example
flutter run -d <your-ios-device-id>

Notes & limitations

  • ReplayKit may not capture protected/DRM content.
  • Preview frames are compressed JPEGs for low-FPS UI/analytics; reading Texture pixels for analytics is not efficient.
  • Avoid requesting frames faster than previewMaxFps.

Troubleshooting

  • Black/very long videos: fixed by starting the writer session at the first sample PTS and using sourceFormatHint.
  • Wrong aspect ratio: fixed by computing output size from the first sample while preserving aspect.
  • Photos save failed: ensure NSPhotoLibraryAddUsageDescription is set and user granted permissions.

License

See LICENSE.

About

A Flutter plugin for iOS that uses ReplayKit to record the device screen and provides low-frequency preview frames (JPEG) for UI and analytics/computer vision. Designed for performance, clarity, and maintainability.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published