如何通过Flutter/Dart实现非自身应用的全屏幕或窗口截图?
实现排除自身应用的屏幕/窗口程序化截图方案
针对你需要的全屏幕、指定应用/窗口截图且排除当前Flutter应用的需求,不同平台的实现逻辑和权限限制不同,以下分平台给出具体方案:
Android平台
- 核心依赖:Android官方的
MediaProjectionAPI,这是捕获其他应用界面的唯一合法途径。 - 权限要求:需要申请
READ_MEDIA_IMAGES(Android 13+)、WRITE_EXTERNAL_STORAGE(低版本),以及用户授权的媒体投影权限。另外Android 10+限制后台截图,必须在前台操作。 - 实现步骤:
- 发起媒体投影权限请求,获取授权后的
MediaProjection实例。 - 通过
MediaProjection创建VirtualDisplay,将屏幕内容渲染到Surface。 - 从
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唯一允许的屏幕捕获方式。 - 限制说明:只能在前台捕获全屏幕,无法直接指定单个应用窗口,需要捕获后手动裁剪掉自身应用区域;必须获得用户明确授权。
- 实现步骤:
- 使用
RPScreenRecorder启动屏幕捕获,获取视频样本缓冲区。 - 将缓冲区转换为
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等现代浏览器。 - 实现步骤:
- 调用
navigator.mediaDevices.getDisplayMedia({video: true})获取屏幕/窗口的媒体流。 - 将流渲染到
<video>元素,通过<canvas>截取帧。 - 获取当前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
相关产品推荐
相关产品推荐

