You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Flutter屏幕截图方法:桌面端仅捕获组件问题及全屏/窗口截图方案

2. Flutter桌面应用中如何截取系统屏幕/窗口(而非仅当前Widget)?

这里要划重点:Flutter自带的API只能捕获应用内部的内容,要截取整个系统屏幕或者其他外部窗口,必须借助各桌面平台的原生能力,通过Platform Channel调用原生API实现,下面分平台给你示例:

Windows平台

可以通过Win32 API实现,推荐用win32包简化调用:

  1. 先在pubspec.yaml添加依赖:
dependencies:
  win32: ^5.0.0
  image: ^4.0.17 # 用来处理位图转PNG
  1. Dart端调用代码:
import 'package:win32/win32.dart';
import 'dart:typed_data';
import 'package:image/image.dart' as img;

Future<Uint8List?> captureWindowsFullScreen() async {
  final hdcScreen = GetDC(NULL);
  final hdcMem = CreateCompatibleDC(hdcScreen);
  final screenRect = GetWindowRect(GetDesktopWindow());
  final width = screenRect.right - screenRect.left;
  final height = screenRect.bottom - screenRect.top;

  // 创建兼容屏幕的位图
  final hBitmap = CreateCompatibleBitmap(hdcScreen, width, height);
  final oldBitmap = SelectObject(hdcMem, hBitmap);

  // 将屏幕内容复制到位图
  BitBlt(hdcMem, 0, 0, width, height, hdcScreen, 0, 0, SRCCOPY);
  SelectObject(hdcMem, oldBitmap);

  // 将位图转换为PNG字节(解决图片倒置问题)
  final bitmapInfo = BITMAPINFO();
  bitmapInfo.bmiHeader.biSize = sizeOf<BITMAPINFOHEADER>();
  bitmapInfo.bmiHeader.biWidth = width;
  bitmapInfo.bmiHeader.biHeight = -height; // 翻转Y轴,避免截图倒置
  bitmapInfo.bmiHeader.biPlanes = 1;
  bitmapInfo.bmiHeader.biBitCount = 32;
  bitmapInfo.bmiHeader.biCompression = BI_RGB;

  final buffer = Uint8List(width * height * 4);
  GetDIBits(hdcScreen, hBitmap, 0, height, buffer, bitmapInfo, DIB_RGB_COLORS);

  final image = img.Image.fromBytes(width, height, buffer, format: img.Format.rgba);
  final pngBytes = img.encodePng(image);

  // 释放系统资源
  DeleteDC(hdcMem);
  ReleaseDC(NULL, hdcScreen);
  DeleteObject(hBitmap);

  return pngBytes;
}

macOS平台

借助Quartz框架实现,通过Platform Channel调用Objective-C代码:

  1. 在macOS Runner项目的RunnerViewController.m中添加平台方法:
#import <Cocoa/Cocoa.h>
#import <Flutter/Flutter.h>

@interface ScreenCapturePlugin : NSObject<FlutterPlugin>
@end

@implementation ScreenCapturePlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
  FlutterMethodChannel* channel = [FlutterMethodChannel
      methodChannelWithName:@"com.yourapp.screencapture"
            binaryMessenger:[registrar messenger]];
  ScreenCapturePlugin* instance = [[ScreenCapturePlugin alloc] init];
  [registrar addMethodCallDelegate:instance channel:channel];
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
  if ([@"captureFullScreen" isEqualToString:call.method]) {
    // 获取主屏幕的截图
    CGDirectDisplayID displayID = CGMainDisplayID();
    CGImageRef screenImage = CGDisplayCreateImage(displayID);
    if (!screenImage) {
      result(FlutterError(@"CaptureFailed", @"无法捕获屏幕内容", nil));
      return;
    }
    // 转换为PNG格式数据
    NSBitmapImageRep* bitmapRep = [[NSBitmapImageRep alloc] initWithCGImage:screenImage];
    NSData* pngData = [bitmapRep representationUsingType:NSPNGFileType properties:@{}];
    CFRelease(screenImage);
    result(pngData);
  } else {
    result(FlutterMethodNotImplemented);
  }
}
@end
  1. 在Dart端调用:
import 'package:flutter/services.dart';

final MethodChannel _screenCaptureChannel = MethodChannel('com.yourapp.screencapture');

Future<Uint8List?> captureMacOSFullScreen() async {
  try {
    return await _screenCaptureChannel.invokeMethod('captureFullScreen');
  } on PlatformException catch (e) {
    print("截图失败:${e.message}");
    return null;
  }
}

Linux平台

使用GDK的截图能力,推荐gtk包:

  1. 添加依赖到pubspec.yaml
dependencies:
  gtk: ^2.0.0
  1. Dart端代码:
import 'package:gtk/gtk.dart';

Future<Uint8List?> captureLinuxFullScreen() async {
  final display = GdkDisplay.getDefault();
  final screen = display.getDefaultScreen();
  final rootWindow = screen.getRootWindow();
  // 获取整个屏幕的Pixbuf对象
  final pixbuf = GdkPixbuf.getFromWindow(rootWindow, 0, 0, screen.getWidth(), screen.getHeight());
  if (pixbuf == null) return null;
  // 保存为PNG字节数据
  return pixbuf.saveToBuffer('png');
}

总结

如果不想自己写原生代码,也可以找找专门的桌面截图第三方库,但这类库数量不多,大部分场景还是需要自己通过Platform Channel对接原生API实现系统级截图。


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

火山引擎 最新活动