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

Flutter Location包二次进入地图页面无法获取当前位置问题

问题分析

你的问题根源在于以下几点:

  • 每次调用getCurrentLocation都创建新的Location实例,且未在页面销毁时取消位置监听,导致资源泄漏,二次进入页面时监听逻辑异常。
  • currentLocation未做null安全处理,页面重建时可能还未获取到位置就强制解包,引发空指针问题(实际代码中此处会直接崩溃)。
  • 二次进入页面时,位置获取的异步流程未正确触发状态更新,导致currentLocation始终为null。
修复方案

修改后的代码如下,关键改动已标注:

import "dart:async";

import "package:dayagreen/ui/widgets/app_bar.dart";
import "package:flutter/material.dart";
import "package:google_maps_flutter/google_maps_flutter.dart";
import "package:location/location.dart";

class ChargingStationScreen extends StatefulWidget {
  const ChargingStationScreen({Key? key}) : super(key: key);

  @override
  State<ChargingStationScreen> createState() => _ChargingStationScreenState();
}

class _ChargingStationScreenState extends State<ChargingStationScreen> {
  final Completer<GoogleMapController> _controller = Completer<GoogleMapController>();

  LocationData? currentLocation;
  BitmapDescriptor? markerIcon = BitmapDescriptor.defaultMarker;
  // 1. 将Location实例提升为成员变量,复用实例避免状态混乱
  final Location _location = Location();
  // 2. 保存位置监听订阅,用于页面销毁时取消
  StreamSubscription<LocationData>? _locationSubscription;

  Future<void> getCurrentLocation() async {
    bool _serviceEnabled;
    PermissionStatus _permissionGranted;

    _serviceEnabled = await _location.serviceEnabled();
    if (!_serviceEnabled) {
      _serviceEnabled = await _location.requestService();
      if (!_serviceEnabled) {
        return;
      }
    }

    _permissionGranted = await _location.hasPermission();
    if (_permissionGranted == PermissionStatus.denied) {
      _permissionGranted = await _location.requestPermission();
      if (_permissionGranted != PermissionStatus.granted) {
        return;
      }
    }

    // 3. 先同步获取当前位置并更新状态
    try {
      final locationData = await _location.getLocation();
      setState(() {
        currentLocation = locationData;
      });
    } catch (e) {
      print("获取当前位置失败: $e");
      return;
    }

    // 4. 根据控制器状态设置监听,避免阻塞
    if (_controller.isCompleted) {
      final googleMapController = await _controller.future;
      _setupLocationListener(googleMapController);
    } else {
      _controller.future.then(_setupLocationListener);
    }
  }

  // 抽离监听逻辑,复用代码并避免重复监听
  void _setupLocationListener(GoogleMapController controller) {
    _locationSubscription?.cancel();
    _locationSubscription = _location.onLocationChanged.listen((newLocation) {
      setState(() {
        currentLocation = newLocation;
        print(currentLocation!.latitude!);
        controller.animateCamera(
          CameraUpdate.newCameraPosition(
            CameraPosition(
              zoom: 14.4746,
              target: LatLng(currentLocation!.latitude!, currentLocation!.longitude!),
            ),
          ),
        );
      });
    });
  }

  void addCustomIcon() {
    BitmapDescriptor.fromAssetImage(const ImageConfiguration(),
            "assets/images/maps_current_location.png")
        .then(
      (icon) {
        setState(() {
          markerIcon = icon;
        });
      },
    );
  }

  @override
  void initState() {
    // addCustomIcon();
    super.initState();
    getCurrentLocation();
  }

  // 5. 页面销毁时取消监听,释放资源
  @override
  void dispose() {
    _locationSubscription?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // 6. 处理位置未获取到的情况,显示加载状态
    if (currentLocation == null) {
      return const Scaffold(
        body: Center(child: CircularProgressIndicator()),
      );
    }

    return Scaffold(
      appBar: const MainAppbar(
        title: "Charging Station",
      ),
      body: GoogleMap(
        mapType: MapType.normal,
        initialCameraPosition: CameraPosition(
          target: LatLng(currentLocation!.latitude!, currentLocation!.longitude!),
          zoom: 14.4746,
        ),
        onMapCreated: (GoogleMapController controller) {
          if (!_controller.isCompleted) {
            _controller.complete(controller);
          }
          // 地图创建后直接更新到当前位置
          if (currentLocation != null) {
            controller.animateCamera(
              CameraUpdate.newCameraPosition(
                CameraPosition(
                  zoom: 14.4746,
                  target: LatLng(currentLocation!.latitude!, currentLocation!.longitude!),
                ),
              ),
            );
          }
        },
        markers: {
          Marker(
            markerId: const MarkerId("current_position"),
            position: LatLng(currentLocation!.latitude!, currentLocation!.longitude!),
            icon: markerIcon!,
          )
        },
      ),
    );
  }
}
关键改动说明
  • 复用Location实例,避免每次创建新实例导致的状态混乱。
  • 新增监听订阅管理,页面销毁时取消订阅,防止内存泄漏和重复监听。
  • 优化位置获取流程,先同步获取当前位置再设置监听,确保页面加载时能快速显示位置。
  • 处理位置未获取到的场景,显示加载指示器避免空指针崩溃。
  • 优化地图控制器逻辑,确保二次进入页面时能正确绑定位置监听。

内容的提问来源于stack exchange,提问作者Rasyid Miftahul Ihsan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 07:27:48