You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中通过Swift代码停止iOS控制中心启动的录屏?

问题:Flutter应用无法停止iOS控制中心启动的屏幕录制

环境

  • Flutter v2.10.5

问题描述

在模拟器和iPhone上测试时,能通过ReplayKit正常检测屏幕录制状态(isRecording返回正确值),但调用停止录屏的方法后,无法终止由iOS控制中心启动的全局屏幕录制。

代码实现

AppDelegate.swift

import UIKit
import Flutter
import MoEngage
import moengage_flutter
import flutter_downloader
import AVFoundation
import Foundation
import ReplayKit

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        if #available(iOS 10.0, *) {
            UNUserNotificationCenter.current().delegate = self
        }
        
        var moSDKConfig : MOSDKConfig
        
        let moAppId = ""
        
        if let config = MoEngage.sharedInstance().getDefaultSDKConfiguration() {
            moSDKConfig = config
            moSDKConfig.moeAppID = moAppId
        } else {
            moSDKConfig = MOSDKConfig.init(appID: moAppId)
        }
        
        moSDKConfig.appGroupID = "group.com.package-name.shared"
        moSDKConfig.moeDataCenter = DATA_CENTER_01
        
        MOFlutterInitializer.sharedInstance.initializeWithSDKConfig(
            moSDKConfig,
            andLaunchOptions: launchOptions
        )
        
        FlutterDownloaderPlugin.setPluginRegistrantCallback(registerPlugins)
        
        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
        
        let isMicrophoneAvailable = FlutterMethodChannel(name: "package-name/audio_recorder",
                                                         binaryMessenger: controller.binaryMessenger)
        isMicrophoneAvailable.setMethodCallHandler({
            [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
            guard call.method == "isMicrophoneAvailable" else {
                result(FlutterMethodNotImplemented)
                return
            }
            self?.isMicrophoneAvailable(result: result)
        })

        let screenRecording = FlutterMethodChannel(name: "package-name/screen_recording",
                                                         binaryMessenger: controller.binaryMessenger)

        screenRecording.setMethodCallHandler({
            [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
            switch call.method {
            case "stop_screen_share", "is_screen_share_active":
                self?.screenRecordActions(call, result)
            default:
                result(FlutterMethodNotImplemented)
            }
        })
        
        GeneratedPluginRegistrant.register(with: self)

        // Set your desired minimumBackgroundFetchInterval
        let backgroundIntervalInMinutes = 60
        UIApplication.shared.setMinimumBackgroundFetchInterval(TimeInterval(60*backgroundIntervalInMinutes))

        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    
    private func isMicrophoneAvailable(result: FlutterResult) {
        if AVAudioSession.sharedInstance().isOtherAudioPlaying == false {
            result(Bool(true))
        } else {
            result(Bool(false))
        }
    }

    // MARK: - Screen Record
    
    private func screenRecordActions(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
        switch call.method {
        case "stop_screen_share":
             RPScreenRecorder.shared().stopCapture( handler: { (error) in
                print("stopping recording");
            })
            
        case "is_screen_share_active":
            let isRecording = RPScreenRecorder.shared().isRecording;
            result(isRecording)
        default:
            print("Not Valid")
        }
    }
}

private func registerPlugins(registry: FlutterPluginRegistry) {
    if (!registry.hasPlugin("FlutterDownloaderPlugin")) {
        FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: "FlutterDownloaderPlugin")!)
    }
}

content_protection.dart

part of '../app_extension.dart';

class QContentProtection {
  QContentProtection._();
  static const platform = MethodChannel('package-name/screen_recording');

  final SecureContent _secureContent = SecureContent();

  void preventScreenshotAndroid(bool prevent) {
    _secureContent.preventScreenshotAndroid(prevent);
  }

  // Future<void> startRecording() async {
  //   try {
  //     await platform.invokeMethod('start_screen_share');
  //   } catch (e, s) {
  //     log('[QContentProtection.startRecording] error : $e, $s');
  //     QAppX.extendedRouter.showNotificationsShade(
  //       message: e.toString(),
  //       type: QInAppNotificationType.failure,
  //     );
  //   }
  // }

  Future<void> stopRecording() async {
    try {
      await platform.invokeMethod('stop_screen_share');
    } catch (e, s) {
      log('[QContentProtection.stopRecording] error : $e, $s');
      QAppX.extendedRouter.showNotificationsShade(
        message: e.toString(),
        type: QInAppNotificationType.failure,
      );
    }
  }

  Future<bool> isScreenRecordingOn() async {
    try {
      final isScreenRecordingOn = await platform.invokeMethod<bool>('is_screen_share_active');
      log('[QContentProtection.isScreenRecordingOn] isScreenRecordingOn : $isScreenRecordingOn');
      return isScreenRecordingOn ?? false;
    } catch (e, s) {
      log('[QContentProtection.isScreenRecordingOn] error : $e, $s');
      QAppX.extendedRouter.showNotificationsShade(
        message: e.toString(),
        type: QInAppNotificationType.failure,
      );
      return false;
    }
  }
}

main.dart

onPressed: () async {
    final isScreenRecordingOn = await QAppX.contentProtection.isScreenRecordingOn();
    if (isScreenRecordingOn) {
        QAppX.contentProtection.stopRecording();
    }
}

解答

核心原因

ReplayKit的stopCapture()方法只能终止由当前应用启动的屏幕录制会话,无法停止通过iOS控制中心启动的系统级全局录屏。这是iOS的权限限制:控制中心启动的录屏属于系统级操作,第三方应用没有权限干预或终止它。

代码检查

你的代码逻辑整体没问题:

  • isRecording能正确检测录屏状态,说明ReplayKit的状态监听正常
  • stopCapture()的调用方式符合官方文档要求,但因为目标录屏会话不是由你的应用启动,所以调用后不会产生效果
  • 另外,main.dart中的按钮点击回调原代码缺少async关键字,会导致编译错误,已在代码块中修正

替代方案

既然无法主动停止控制中心的录屏,你可以:

  1. 检测到录屏状态时,在应用内弹出提示,引导用户手动从控制中心停止录屏
  2. 对敏感内容启用屏幕保护,防止录屏捕获敏感信息

内容的提问来源于stack exchange,提问作者codenameakshay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 21:54:14