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

如何通过Flutter/Dart实现非自身应用的全屏幕或窗口截图?

实现排除自身应用的屏幕/窗口程序化截图方案

针对你需要的全屏幕、指定应用/窗口截图且排除当前Flutter应用的需求,不同平台的实现逻辑和权限限制不同,以下分平台给出具体方案:

Android平台

  • 核心依赖:Android官方的MediaProjection API,这是捕获其他应用界面的唯一合法途径。
  • 权限要求:需要申请READ_MEDIA_IMAGES(Android 13+)、WRITE_EXTERNAL_STORAGE(低版本),以及用户授权的媒体投影权限。另外Android 10+限制后台截图,必须在前台操作。
  • 实现步骤:
    1. 发起媒体投影权限请求,获取授权后的MediaProjection实例。
    2. 通过MediaProjection创建VirtualDisplay,将屏幕内容渲染到Surface。
    3. 从Surface提取Bitmap,再获取当前Flutter应用窗口的位置和尺寸,裁剪掉对应区域。
  • 关键代码片段(需通过MethodChannel调用原生Android代码):
    // 申请屏幕捕获权限
    MediaProjectionManager projectionManager = 
        (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
    Intent captureIntent = projectionManager.createScreenCaptureIntent();
    startActivityForResult(captureIntent, 1001);
    
    // 在onActivityResult中处理授权结果
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 1001 && resultCode == RESULT_OK) {
            MediaProjection mediaProjection = projectionManager.getMediaProjection(resultCode, data);
            // 创建VirtualDisplay并捕获帧,后续处理Bitmap裁剪
        }
    }
    

iOS平台

  • 核心依赖:Apple的ReplayKit框架,这是iOS唯一允许的屏幕捕获方式。
  • 限制说明:只能在前台捕获全屏幕,无法直接指定单个应用窗口,需要捕获后手动裁剪掉自身应用区域;必须获得用户明确授权。
  • 实现步骤:
    1. 使用RPScreenRecorder启动屏幕捕获,获取视频样本缓冲区。
    2. 将缓冲区转换为UIImage,获取当前应用窗口的位置,裁剪掉对应区域。
  • 关键代码片段(通过MethodChannel调用原生iOS代码):
    RPScreenRecorder *recorder = [RPScreenRecorder sharedRecorder];
    [recorder startCaptureWithHandler:^(CMSampleBufferRef sampleBuffer, RPScreenRecorderError error, BOOL *stop) {
        if (error) {
            NSLog(@"捕获错误: %@", error.localizedDescription);
            return;
        }
        // 将样本缓冲区转为UIImage
        UIImage *fullScreenshot = [self imageFromSampleBuffer:sampleBuffer];
        // 获取当前应用窗口区域并裁剪
        CGRect appFrame = [[UIApplication sharedApplication] keyWindow].frame;
        UIImage *croppedImage = [self cropImage:fullScreenshot toRect:CGRectMake(0, 0, fullScreenshot.size.width, appFrame.origin.y)];
        // 拼接其他非应用区域,最终得到排除自身的截图
        // 回调给Flutter端
    } completionHandler:^(NSError *error) {
        if (error) {
            NSLog(@"授权失败: %@", error.localizedDescription);
        }
    }];
    

Web平台

  • 核心依赖:浏览器标准API getDisplayMedia,支持Chrome、Firefox等现代浏览器。
  • 实现步骤:
    1. 调用navigator.mediaDevices.getDisplayMedia({video: true})获取屏幕/窗口的媒体流。
    2. 将流渲染到<video>元素,通过<canvas>截取帧。
    3. 获取当前Flutter Web应用的DOM元素位置,裁剪掉对应区域。
  • Dart端关键代码:
    import 'dart:js' as js;
    import 'dart:html' as html;
    
    Future<void> captureAndCropScreen() async {
      try {
        // 请求屏幕捕获权限
        final mediaStream = await js.context.callMethod('navigator.mediaDevices.getDisplayMedia', [{'video': true}]);
        final video = html.VideoElement()
          ..srcObject = mediaStream
          ..autoplay = true;
        // 等待视频加载完成
        await Future.delayed(const Duration(milliseconds: 150));
        
        final canvas = html.CanvasElement(
          width: video.videoWidth,
          height: video.videoHeight,
        );
        final ctx = canvas.context2D;
        ctx.drawImage(video, 0, 0);
        
        // 获取当前Flutter应用容器的位置
        final appElement = html.document.getElementById('flutter-container');
        if (appElement == null) return;
        final appRect = appElement.getBoundingClientRect();
        
        // 创建裁剪后的画布,绘制非应用区域
        final croppedCanvas = html.CanvasElement(
          width: canvas.width,
          height: canvas.height,
        );
        final croppedCtx = croppedCanvas.context2D;
        
        // 绘制顶部区域
        croppedCtx.drawImageScaledFromSource(
          canvas, 0, 0, canvas.width, appRect.top.toInt(),
          0, 0, canvas.width, appRect.top.toInt(),
        );
        // 绘制左侧区域
        croppedCtx.drawImageScaledFromSource(
          canvas, 0, appRect.top.toInt(), appRect.left.toInt(), appRect.height.toInt(),
          0, appRect.top.toInt(), appRect.left.toInt(), appRect.height.toInt(),
        );
        // 绘制右侧区域
        croppedCtx.drawImageScaledFromSource(
          canvas, appRect.right.toInt(), appRect.top.toInt(), canvas.width - appRect.right.toInt(), appRect.height.toInt(),
          appRect.right.toInt(), appRect.top.toInt(), canvas.width - appRect.right.toInt(), appRect.height.toInt(),
        );
        // 绘制底部区域
        croppedCtx.drawImageScaledFromSource(
          canvas, 0, appRect.bottom.toInt(), canvas.width, canvas.height - appRect.bottom.toInt(),
          0, appRect.bottom.toInt(), canvas.width, canvas.height - appRect.bottom.toInt(),
        );
        
        // 获取裁剪后的图片数据(可转为base64或Blob)
        final croppedImageData = croppedCanvas.toDataUrl();
        print(croppedImageData);
        
        // 停止媒体流
        mediaStream.getTracks().forEach((track) => track.stop());
      } catch (e) {
        print('捕获失败: $e');
      }
    }
    

通用注意事项

  • 所有平台都需要用户明确授权才能进行屏幕捕获,无法静默操作。
  • 跨平台开发建议通过MethodChannel封装各平台的原生逻辑,在Dart层统一调用,减少代码冗余。
  • 若需要指定单个应用窗口截图,仅桌面平台(Windows/macOS/Linux)支持,移动端和Web只能捕获全屏幕后裁剪。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:05:57