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

google_maps_flutter_web页面重载释放控制器报错求助

解决google_maps_flutter_web网页重载时dispose阶段的断言错误

核心解决方案

  • 彻底清理定时器资源,避免Widget销毁后仍有任务执行
  • 在所有更新UI的逻辑前增加mounted检查,防止触发已销毁Widget的状态更新
  • 可选:将google_maps_flutter升级至最新稳定版本,官方可能已修复该web端的生命周期bug

修改后的完整代码

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:jm_connect_front/services/machines.dart';
import 'package:provider/provider.dart';
import '../../models/machine_model.dart';
import '../../providers/machine_provider.dart';

class JMMap extends StatefulWidget {
  const JMMap({super.key});

  @override
  State<JMMap> createState() => JMMapState();
}

class JMMapState extends State<JMMap> {
  late final Completer<GoogleMapController> _controller = Completer();
  List<MachineModel>? allMachines;
  Map<MarkerId, Marker> markers = {};
  Timer? timer;
  BitmapDescriptor? machineIcon;
  BitmapDescriptor? machineOnIcon;

  @override
  void initState() {
    super.initState();
    _setCustomMarker();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      timer = Timer.periodic(const Duration(seconds: 1), (currentTimer) async {
        // 定时器已被取消或Widget已销毁,直接终止执行
        if (!mounted || timer != currentTimer) return;
        
        await Machines().getMachines(context);
        if (!mounted) return;
        
        allMachines = Provider.of<MachineProvider>(context, listen: false).machineItems;
        updateMarkers();
      });
    });
  }

  void _setCustomMarker() async {
    machineIcon = await BitmapDescriptor.fromAssetImage(
        const ImageConfiguration(), 'assets/images/machine_marker.png');
    machineOnIcon = await BitmapDescriptor.fromAssetImage(
        const ImageConfiguration(), 'assets/images/machine_marker_on.png');
  }

  @override
  void dispose() {
    // 取消定时器并置空,避免后续引用
    timer?.cancel();
    timer = null;
    // 清空标记,防止后续残留任务触发地图更新
    setState(() {
      markers.clear();
    });
    super.dispose();
  }

  void updateMarkers() {
    // Widget已销毁,直接返回
    if (!mounted) return;
    
    final newMarkers = <MarkerId, Marker>{};
    if (allMachines == null) return;
    
    for (var machine in allMachines!) {
      int lastUpdate = DateTime.now()
          .difference(DateTime.parse(machine.updatedAt.toString()))
          .inMinutes;
      final markerId = MarkerId(machine.id.toString());
      final marker = Marker(
        markerId: markerId,
        position: LatLng(machine.latitude, machine.longitude),
        icon: lastUpdate > 2 ? machineIcon! : machineOnIcon!,
      );

      newMarkers[markerId] = marker;
    }

    setState(() {
      markers.clear();
      markers.addAll(newMarkers);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GoogleMap(
        mapType: MapType.hybrid,
        buildingsEnabled: false,
        compassEnabled: false,
        liteModeEnabled: false,
        indoorViewEnabled: false,
        mapToolbarEnabled: false,
        trafficEnabled: false,
        myLocationEnabled: false,
        tiltGesturesEnabled: false,
        zoomControlsEnabled: false,
        myLocationButtonEnabled: false,
        fortyFiveDegreeImageryEnabled: false,
        markers: Set<Marker>.of(markers.values),
        initialCameraPosition: CameraPosition(
          target: LatLng(
            allMachines?.isNotEmpty == true
                ? allMachines![0].latitude
                : -20.90381991398391,
            allMachines?.isNotEmpty == true
                ? allMachines![0].longitude
                : -47.59624569562974,
          ),
          zoom: 18,
        ),
        onMapCreated: (GoogleMapController controller) {
          _controller.complete(controller);
        },
      ),
    );
  }
}

错误原因分析

报错的核心是网页重载时,Widget的dispose方法执行后,之前启动的定时器任务仍可能在微任务队列中等待执行。此时调用updateMarkers触发setState,会尝试更新GoogleMap的标记,但web端插件内部的GoogleMapController已经被释放,导致controller != null的断言失败,抛出Maps cannot be retrieved before calling buildView!错误。

通过在定时器回调、标记更新逻辑中增加mounted检查,以及在dispose时彻底清理定时器和标记数据,可以避免在Widget销毁后触发地图相关的更新操作,从而解决该错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 07:32:10