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

Flutter Material App切换场景调试时渲染报错求助

场景切换时Google Maps页面出现渲染错误分析

我开发了两个包含Google Maps并绘制Polyline的场景,应用运行正常,但调试时会出现大量报错栈信息,错误提示为**"Failed to rasterize a picture: unable to create render target at specified size"**,报错无固定触发条件,在使用Navigator切换场景时出现。请求帮忙分析原因。

调试错误信息

════════ Exception caught by rendering library ═════════════════════════════════
Failed to rasterize a picture: unable to create render target at specified size.
The callstack when the image was created was:
#0      new Image._.<anonymous closure> (dart:ui/painting.dart:1670:32)
#1      new Image._ (dart:ui/painting.dart:1672:6)
#2      Scene.toImageSync (dart:ui/compositing.dart:34:18)
#3      OffsetLayer.toImageSync
layer.dart:1497
#4      _RenderSnapshotWidget._paintAndDetachToImage
snapshot_widget.dart:315
#5      _RenderSnapshotWidget.paint
snapshot_widget.dart:345
#6      RenderObject._paintWithContext
object.dart:2853
#7      PaintingContext.paintChild
object.dart:253
#8      RenderProxyBoxMixin.paint
#9      _ZoomEnterTransitionPainter.paint
page_transitions_theme.dart:866
#10     _RenderSnapshotWidget.paint
snapshot_widget.dart:335
#11     RenderObject._paintWithContext
object.dart:2853
#12     PaintingContext.paintChild
object.dart:253
#13     RenderProxyBoxMixin.paint
proxy_box.dart:144
#14     _ZoomExitTransitionPainter.paint
page_transitions_theme.dart:938
#15     _RenderSnapshotWidget.paint
snapshot_widget.dart:335
#16     RenderObject._paintWithContext
object.dart:2853
#17     PaintingContext.paintChild
object.dart:253
#18     RenderProxyBoxMixin.paint
proxy_box.dart:144
#19     _ZoomEnterTransitionPainter.paint
page_transitions_theme.dart:866
#20     _RenderSnapshotWidget.paint
snapshot_widget.dart:335
#21     RenderObject._paintWithContext
object.dart:2853
#22     PaintingContext.paintChild
object.dart:253
#23     RenderProxyBoxMixin.paint
proxy_box.dart:144
#24     RenderObject._paintWithContext
object.dart:2853
#25     PaintingContext._repaintCompositedChild
object.dart:169
#26     PaintingContext.repaintCompositedChild
object.dart:112
#27     PipelineOwner.flushPaint
object.dart:1137
#28     RendererBinding.drawFrame
binding.dart:518
#29     WidgetsBinding.drawFrame
binding.dart:865
#30     RendererBinding._handlePersistentFrameCallback
binding.dart:381
#31     SchedulerBinding._invokeFrameCallback
binding.dart:1289
#32     SchedulerBinding.handleDrawFrame
binding.dart:1218
#33     SchedulerBinding._handleDrawFrame
binding.dart:1076
#34     _invoke (dart:ui/hooks.dart:145:13)
#35     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:338:5)
#36     _drawFrame (dart:ui/hooks.dart:112:31)

The relevant error-causing widget was
MaterialApp

导航页面代码

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_in_flutter/location_service.dart';
import 'package:geolocator/geolocator.dart';
import 'package:label_marker/label_marker.dart';

StreamSubscription<Position>? userStreamlocation;
Position? streamResult;

class navScreen extends StatefulWidget {
  final String userOrigin;
  final String userGoal;  

  navScreen({Key? mykey, required this.userOrigin, required this.userGoal});

  @override
  State<navScreen> createState() => nav_Screen(userOrigin: userOrigin, userGoal: userGoal);
}

class geostracking {
  Future<Position> determineLocation() async {
    bool serviceEnabled;
    LocationPermission permission;
    serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      return Future.error('Location Service are Disabled');
    }
    permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        return Future.error('Location Permission Denied');
      }
    }
    if (permission == LocationPermission.deniedForever) {
      return Future.error('Permission Denied Forever');
    }

    Position position = await Geolocator.getCurrentPosition();
    
    print(position);
    return position;
  }

  Position? listenToLocation() {
    Position? userposition;
    final LocationSettings _locationSettings = LocationSettings(accuracy: LocationAccuracy.high,distanceFilter: 100);
    userStreamlocation = Geolocator.getPositionStream(locationSettings: _locationSettings).listen(
    (userposition) {
        print(userposition == null ? 'Unknown' : '${userposition.latitude.toString()}, ${userposition.longitude.toString()}');
        setState(){
          streamResult=userposition;
        }
    });
    return userposition;
  }  
}

class nav_Screen extends State<navScreen> {
  StreamSubscription<Position>? streamPosition;

  @override
  void dispose() {
    super.dispose();
    streamPosition?.cancel();
  }

  late GoogleMapController mapController;
  final String userOrigin;
  final String userGoal;  

  nav_Screen({required this.userOrigin,required this.userGoal,  });

  final LatLng _kGooglePlex = LatLng(baseLat, baseLong);
  Set<Marker> _markers = Set<Marker>();
  Set<Polyline> _navPolylines =Set<Polyline>();
  int _polylineIdCounter=0;
  int markerNumber=1;
  int _markerIDCounter = 1;

  void _setMarker(String ID, var Place) {
    String markerLabel;
    if(markerNumber==1) {
      markerLabel = 'Start';
    } else {
      markerLabel = "Destination";
    }
    setState(() {
      _markers.addLabelMarker(LabelMarker(
        backgroundColor: Color.fromARGB(255, 0, 8, 255),
        label: markerLabel,
        markerId: MarkerId(ID),
        position: LatLng(Place['geometry']['location']['lat'],
            Place['geometry']['location']['lng']),)            
      );
      _markerIDCounter++;
      markerNumber++;
    });
    
  }

  void _setPolylines(List<PointLatLng> points) {
    final String _navPolylinesID = 'polyline_$_polylineIdCounter';
    //print('Check : '+points.toString());
    _navPolylines.add(
      Polyline(
        polylineId: PolylineId(_navPolylinesID),
        width: 5,
        color: Color.fromARGB(255, 56, 12, 255),
        startCap: Cap.roundCap,
        endCap: Cap.buttCap,
        visible: true,
        points: points
            .map((point) => LatLng(point.latitude, point.longitude)).toList(),
      )    
    );
    _polylineIdCounter++; 
  }

  void _onMapCreated(GoogleMapController controller) async {
    mapController = controller;
    var direction = await LocationService().getDirectionNoWP(userOrigin, userGoal); 
    
    print(_polylineIdCounter);
    _setPolylines(direction['polyline_decoded']); 

    String OplaceID = await LocationService().getPlaceID(userOrigin);
    var Oplace = await LocationService().getPlace(userOrigin);      
    _setMarker(OplaceID, Oplace);
    String DplaceID = await LocationService().getPlaceID(userGoal);
    var Dplace = await LocationService().getPlace(userGoal);      
    _setMarker(DplaceID, Dplace);
    setState(() {});           
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Test')),
      body: Column(
        children: [
          Expanded(
            child: GoogleMap(
              mapType: MapType.normal,
              initialCameraPosition: CameraPosition(target: _kGooglePlex,zoom: 10),
              markers: _markers,
              polylines: _navPolylines,
              onMapCreated: _onMapCreated,
            )
          ),
        ]
      ),
      floatingActionButton: Container(
        alignment: Alignment.bottomCenter,
        child: FloatingActionButton(
          onPressed: ()async{
            geostracking().listenToLocation();
          },
          child: Icon(Icons.navigation),
        ),
      ),
    );
  }
}

主页面代码片段

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_in_flutter/current_location.dart';
import 'package:google_maps_in_flutter/customer_profile.dart';
import 'package:google_maps_in_flutter/location_service.dart';
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:geolocator/geolocator.dart';
import 'package:label_marker/label_marker.dart';

void main() => runApp(MyApp());

StreamSubscription<Position>? userStreamlocation;
Position? streamResult;

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Google Map Test',
      home: GMap(),
      //routes: ,
    );
  }
}

class GMap extends StatefulWidget {
  @override
  State<GMap> createState() => GMapState();
}

class requestTemplateConverter{}

class GMapState extends State<GMap> {

错误原因分析及解决方案

核心问题定位

这个错误属于Flutter渲染层的内存/资源分配问题,当Navigator切换场景时,默认的页面过渡动画(这里是ZoomEnter/ExitTransition)需要对页面内容进行快照渲染,但Google Maps组件本身占用大量GPU资源,加上Polyline和自定义LabelMarker的渲染负载,导致系统无法在指定尺寸下创建渲染目标。

具体诱因

  1. 全局流订阅未正确管理:userStreamlocation是全局变量,切换页面时未取消旧订阅,多个位置监听流同时运行,持续触发UI重绘,加重渲染负担。
  2. 页面过渡动画与地图渲染冲突:默认缩放过渡动画需要对整个地图页面进行快照,而地图组件的渲染尺寸可能超过当前GPU的纹理分配限制。
  3. 资源未及时释放:nav_Screen仅取消了streamPosition,但全局的userStreamlocation未在页面dispose时清理,导致内存泄漏和不必要的渲染触发。

修复步骤

  1. 清理全局流订阅:
    在nav_Screen的dispose方法中添加全局流的取消逻辑:

    @override
    void dispose() {
      super.dispose();
      streamPosition?.cancel();
      userStreamlocation?.cancel(); // 取消全局位置监听流
    }
    

    同时避免使用全局变量管理流订阅,将userStreamlocation移到nav_Screen类内部,确保每个页面实例的订阅独立。

  2. 禁用页面过渡动画或调整动画类型:
    切换页面时使用无过渡或轻量级过渡,避免地图快照:

    Navigator.push(context, PageRouteBuilder(
      pageBuilder: (context, animation, secondaryAnimation) => navScreen(...),
      transitionsBuilder: (context, animation, secondaryAnimation, child) {
        return child; // 直接跳转,无过渡动画
      },
    ));
    

    或者使用更轻量的过渡(如淡入淡出)替代缩放过渡。

  3. 优化地图渲染资源:

    • 避免在onMapCreated中一次性添加大量Polyline点,如果路线过长,考虑分段加载或简化路线。
    • 检查label_marker插件的实现,是否存在内存泄漏或过度渲染问题,尝试替换为原生Marker+InfoWindow的组合。
  4. 确保位置监听仅在需要时运行:
    将geostracking类的listenToLocation方法返回的流订阅交给页面管理,而非全局变量,确保页面销毁时及时取消。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 16:46:15