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:仅截取地图区域
如果只需要地图部分的截图,可通过「全屏截图+区域裁剪」实现:
- 给地图组件绑定GlobalKey获取位置和尺寸:
final GlobalKey _mapKey = GlobalKey(); // 地图组件添加key: MapWidget( key: _mapKey, // 其他配置 )
- 获取地图在屏幕上的坐标和尺寸:
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的全屏截图方法,再裁剪出地图区域:
// 将全屏截图转成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
相关产品推荐
相关产品推荐

