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

Flutter平台视图截图求助:地图组件截图显示空白

解决Flutter平台视图(地图)截图空白问题

你遇到的错误核心原因是:平台视图(如各类地图组件)由原生系统渲染,不属于Flutter的光栅化渲染树,所以Flutter自带的RenderRepaintBoundary以及基于它的第三方截图包(Screenshot、Take Screenshot等)都无法捕获其内容,只会返回空白区域。

以下是两种可行的解决方案:

方案1:调用原生API实现全屏截图

这种方法能获取和手机系统截图完全一致的内容,包含所有平台视图和Flutter组件。

Android端实现

使用Android官方的MediaProjection API(需用户授权屏幕捕获权限),通过MethodChannel和Flutter交互:

class MainActivity : FlutterActivity() {
    private var mediaProjectionManager: MediaProjectionManager? = null
    private var mediaProjection: MediaProjection? = null
    private var resultCode: Int = 0
    private var data: Intent? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mediaProjectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
        MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, "screenshot_channel").setMethodCallHandler { call, result ->
            when (call.method) {
                "requestPermission" -> {
                    startActivityForResult(mediaProjectionManager!!.createScreenCaptureIntent(), 1000)
                    result.success(true)
                }
                "takeFullScreenshot" -> {
                    mediaProjection ?: run {
                        result.error("NO_PERMISSION", "未获取屏幕捕获权限", null)
                        return@setMethodCallHandler
                    }
                    val metrics = resources.displayMetrics
                    val width = metrics.widthPixels
                    val height = metrics.heightPixels
                    val imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 1)
                    
                    mediaProjection!!.createVirtualDisplay(
                        "Screenshot", width, height, metrics.densityDpi,
                        DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                        imageReader.surface, null, null
                    )
                    
                    imageReader.setOnImageAvailableListener({ reader ->
                        val image = reader.acquireLatestImage() ?: return@setOnImageAvailableListener
                        val buffer = image.planes[0].buffer
                        val bytes = ByteArray(buffer.capacity())
                        buffer.get(bytes)
                        image.close()
                        
                        val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
                        val outputStream = ByteArrayOutputStream()
                        bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
                        result.success(outputStream.toByteArray())
                    }, Handler(Looper.getMainLooper()))
                }
                else -> result.notImplemented()
            }
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 1000 && resultCode == RESULT_OK) {
            this.resultCode = resultCode
            this.data = data
            mediaProjection = mediaProjectionManager!!.getMediaProjection(resultCode, data!!)
        }
    }
}

iOS端实现

使用UIGraphicsImageRenderer捕获屏幕内容,同样通过MethodChannel交互:

import UIKit
import Flutter

class MainViewController: FlutterViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let channel = FlutterMethodChannel(name: "screenshot_channel", binaryMessenger: flutterEngine!.binaryMessenger)
        channel.setMethodCallHandler { call, result in
            switch call.method {
            case "takeFullScreenshot":
                guard let window = UIApplication.shared.windows.first else {
                    result.error("NO_WINDOW", "无法获取主窗口", nil)
                    return
                }
                let renderer = UIGraphicsImageRenderer(size: window.bounds.size)
                let image = renderer.image { context in
                    window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
                }
                guard let pngData = image.pngData() else {
                    result.error("CONVERT_FAILED", "图片转PNG失败", nil)
                    return
                }
                result.success(pngData)
            default:
                result.notImplemented()
            }
        }
    }
}

Flutter端调用

final MethodChannel _screenshotChannel = MethodChannel('screenshot_channel');

// Android需先请求权限,iOS无需
if (Platform.isAndroid) {
  await _screenshotChannel.invokeMethod('requestPermission');
}

// 获取全屏截图字节流
Uint8List? fullScreenshotBytes = await _screenshotChannel.invokeMethod('takeFullScreenshot');

// 后续可将字节流转为图片显示或保存

方案2:仅截取地图区域

如果只需要地图部分的截图,可通过「全屏截图+区域裁剪」实现:

  1. 给地图组件绑定GlobalKey获取位置和尺寸:
final GlobalKey _mapKey = GlobalKey();

// 地图组件添加key:
MapWidget(
  key: _mapKey,
  // 其他配置
)
  1. 获取地图在屏幕上的坐标和尺寸:
RenderBox? mapRenderBox = _mapKey.currentContext?.findRenderObject() as RenderBox?;
if (mapRenderBox == null) return;

Offset mapOffset = mapRenderBox.localToGlobal(Offset.zero);
Size mapSize = mapRenderBox.size;
final pixelRatio = MediaQuery.of(context).devicePixelRatio;
  1. 调用方案1的全屏截图方法,再裁剪出地图区域:
// 将全屏截图转成Image
ui.Image fullImage = await ui.decodeImageFromList(fullScreenshotBytes!);

// 裁剪地图区域
final recorder = ui.PictureRecorder();
final canvas = Canvas(recorder);
Rect cropRect = Rect.fromLTWH(
  mapOffset.dx * pixelRatio,
  mapOffset.dy * pixelRatio,
  mapSize.width * pixelRatio,
  mapSize.height * pixelRatio,
);

canvas.drawImageRect(
  fullImage,
  cropRect,
  Rect.fromLTWH(0, 0, mapSize.width * pixelRatio, mapSize.height * pixelRatio),
  Paint(),
);

final picture = recorder.endRecording();
ui.Image croppedMapImage = await picture.toImage(
  (mapSize.width * pixelRatio).toInt(),
  (mapSize.height * pixelRatio).toInt(),
);

// 转成PNG字节流
ByteData? byteData = await croppedMapImage.toByteData(format: ui.ImageByteFormat.png);
Uint8List? mapScreenshotBytes = byteData?.buffer.asUint8List();

关键提示

  • 所有Flutter端的截图方案都无法捕获平台视图,必须依赖原生系统API。
  • Android的MediaProjection需要用户手动授权,这是系统强制要求,无法绕过。
  • 该方案适用于任何类型的平台视图(谷歌地图、高德地图、百度地图等),无需依赖特定SDK的截图接口。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 02:20:38