Flutter iOS - 在加载 Google Maps 小部件之前无法获取 initialCameraPosition 的当前位置

Posted

技术标签:

【中文标题】Flutter iOS - 在加载 Google Maps 小部件之前无法获取 initialCameraPosition 的当前位置【英文标题】:Flutter iOS - cannot get current location for initialCameraPosition before loading Google Maps widget 【发布时间】:2020-08-02 09:15:34 【问题描述】:

我正在尝试在我的 Flutter ios 应用程序中实现一个简单的地图功能。我遵循了从Flutter location package readme 到Medium "Implement Real-time Location Updates on Google Maps in your Flutter Apps" article 的指南,最后解决了许多关于类似问题的堆栈问题。

问题是我在加载地图之前无法获取当前位置,因此在 initialCameraPosition 中获取其 LatLng 会导致 NPE。我可以做的是加载带有硬编码initialCameraPosition的地图,然后正确加载当前位置,我可以通过拖动或单击右下角的浮动位置按钮将相机移动到它。

这是我的代码示例:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';

const double CAMERA_ZOOM = 16;
const LatLng SOURCE_LOCATION = LatLng(37.3317, -122.0325086);

class MapPage extends StatefulWidget 
  @override
  _MapPageState createState() => _MapPageState();


class _MapPageState extends State<MapPage> 
  GoogleMapController mapController;
  LocationData currentLocation;
  Location location;
  bool _serviceEnabled;
  PermissionStatus _permissionGranted;

  @override
  void initState() 
    super.initState();
    location = new Location();

    location.onLocationChanged.listen((event) 
      currentLocation = event;
    );

    setInitialLocation();
  

  void setInitialLocation() async 
    _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;
      
    

    currentLocation =  await location.getLocation();
  

  void _onMapCreated(GoogleMapController controller) 
    mapController = controller;
  


  CameraPosition initialCameraPosition() 
    LatLng target;
    if (currentLocation != null)
      target = LatLng(currentLocation.latitude, currentLocation.longitude);
    else
      target = SOURCE_LOCATION;
    return CameraPosition(target: target, zoom: CAMERA_ZOOM);
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.orangeAccent,
        title: Text('Map'),
        centerTitle: true,
      ),
      body:
      GoogleMap(
      onMapCreated: _onMapCreated,
      initialCameraPosition: initialCameraPosition(),
      myLocationEnabled: true,
      ),
    );
  

我知道获取当前位置是一个异步过程,所以我也尝试在正文部分使用FutureBuilder

      FutureBuilder<LocationData>(
        future: currentLocation,
        builder: (context, snapshot) 
          if (snapshot.hasData) 
            LocationData currentLocation = snapshot.data;
            return GoogleMap(
              onMapCreated: _onMapCreated,
              initialCameraPosition: CameraPosition(
                  target: LatLng(
                      currentLocation.latitude, currentLocation.longitude)),
              myLocationEnabled: true,
            );
           else if (snapshot.hasError) 
            return (Text("$snapshot.error"));
          
          return Center(
            child: CircularProgressIndicator(
              backgroundColor: Colors.orangeAccent,
            ),
          );
        ,
      ),

当我不得不从 http GET 请求中检索数据时,它的作用就像一个魅力,但这次它只会导致无穷无尽的CircularProgressIndicator

我从堆栈问题尝试了许多其他解决方案,包括当currentLocation 为空时返回空Container,为FutureBuilder snapshot.connectionState 创建条件,同样没有成功。

有趣的是,当我深入研究Location.getCurrentLocation() 方法时,我发现:

/// Gets the current location of the user.
  ///
  /// Throws an error if the app has no permission to access location.
  /// Returns a [LocationData] object.
  Future<LocationData> getLocation() async 
    return LocationPlatform.instance.getLocation();
  

然后转到:

/// Gets the current location of the user.
  ///
  /// Throws an error if the app has no permission to access location.
  /// Returns a [LocationData] object.
  Future<LocationData> getLocation() 
    throw UnimplementedError();
  

这让我怀疑这个插件是否可以工作。

这是我的flutter doctor -v 结果:

[✓] Flutter (Channel master, v1.18.0-6.0.pre.82, on Mac OS X 10.15.4 19E287, locale pl-PL)
    • Flutter version 1.18.0-6.0.pre.82 at /Users/Vuco/flutter
    • Framework revision f35b673f2b (26 hours ago), 2020-04-19 02:45:01 +0530
    • Engine revision a5e0b2f2f2
    • Dart version 2.9.0 (build 2.9.0-1.0.dev 5b19445d9c)


[✓] android toolchain - develop for Android devices (Android SDK version 29.0.3)
    • Android SDK at /Users/Vuco/Library/Android/sdk
    • Platform android-29, build-tools 29.0.3
    • Java binary at: /Users/Vuco/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/192.6308749/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 11.4.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 11.4.1, Build version 11E503a
    • CocoaPods version 1.9.1

[✓] Android Studio (version 3.6)
    • Android Studio at /Users/Vuco/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/192.6308749/Android Studio.app/Contents
    • Flutter plugin version 45.0.1
    • Dart plugin version 192.7761
    • Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)

[!] IntelliJ IDEA Ultimate Edition (version 2020.1)
    • IntelliJ at /Users/Vuco/Applications/JetBrains Toolbox/IntelliJ IDEA Ultimate.app
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
    • For information about installing plugins, see
      https://flutter.dev/intellij-setup/#installing-the-plugins

[✓] VS Code (version 1.44.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.9.1

[✓] Connected device (1 available)
    • iPhone 8 • F60856B8-2C6E-401D-A0F9-06FFC7E09876 • ios • com.apple.CoreSimulator.SimRuntime.iOS-13-4 (simulator)

! Doctor found issues in 1 category.

以及我使用的依赖项:

google_maps_flutter: ^0.5.25+3
location: ^3.0.2

【问题讨论】:

【参考方案1】:

您在获得初始位置之前渲染地图的问题,我相信您意识到FutureBuilder 会起作用,但可能会过度杀戮。您真正需要的只是一个布尔值。

GoogleMapController mapController;
  LocationData currentLocation;
  Location location;
  bool _serviceEnabled;
  PermissionStatus _permissionGranted;
  CameraPosition _center =
      CameraPosition(target: SOURCE_LOCATION, zoom: CAMERA_ZOOM);
  bool isLoading = false;

  @override
  void initState() 
    super.initState();
    location = new Location();

    _getLocation();
  

  _getLocation() async 
    setState(() 
      isLoading = true;
    );
    initialize();
    currentLocation = await location.getLocation();
    if (currentLocation == null) 
      return;
    
    _center = CameraPosition(
        target: LatLng(currentLocation.latitude, currentLocation.longitude),
        zoom: CAMERA_ZOOM);

    setState(() 
      isLoading = false;
    );

    print("CurrentLocation: $currentLocation");
  

  Future<void> initialize() async 
    _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;
      
    
  

  void _onMapCreated(GoogleMapController controller) 
    mapController = controller;
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.orangeAccent,
        title: Text('Map'),
        centerTitle: true,
      ),
      body: isLoading
          ? CircularProgressIndicator()
          : Container(
              child: GoogleMap(
                onMapCreated: _onMapCreated,
                initialCameraPosition: _center,
                myLocationEnabled: true,
              ),
            ),
    );
  

【讨论】:

按照您的建议解决了我的问题,非常感谢! 没问题很高兴帮助 getLocation() 函数永远存在并且永远不会返回我按照上述所有步骤操作,授予权限,它只是不继续并且不返回 null 这里发生了什么? @RagehElAzzazy 我已经有一段时间没有在 Flutter 中工作了,但是您是否有可能忘记生成 Google Maps API 密钥并将其添加到您的项目中?我当时使用的 google_maps_flutter 版本还需要向 Info.plist(适用于 iOS)添加权限消息,但这可能会在当前版本中发生变化,因为我现在在任何地方都看不到该说明。您是否关注过我在帖子中链接的 Medium 文章? 与@RagehElAzzazy 相同。仍然无法在 iOS 上运行。【参考方案2】:

我建议改用Geolocator 包。这是我在获取位置和订阅位置所需的所有应用程序中使用的,并且有据可查。尝试使用它,如果您的 FutureBuilder 与该软件包仍然存在问题,请使用您的新代码更新帖子,我可以尝试进一步帮助您。

如果您在构建地图之前没有初始化和检索位置,则可以使用 FutureBuilder。

【讨论】:

以上是关于Flutter iOS - 在加载 Google Maps 小部件之前无法获取 initialCameraPosition 的当前位置的主要内容,如果未能解决你的问题,请参考以下文章

Flutter Google Maps 插件在 iOS 上无法正常工作

使用 Google 登录无法在 Android 上运行(仅在 iOS 上) - Flutter

Flutter for Web 中的 WebView 未加载 Google 地图或其他

在 iOS 上退出 Google 登录无法在 Flutter Firebase 中运行

使用 google_mobile_ads 加载横幅时 Flutter App 崩溃

MissingPluginException(在通道 plugins.flutter.io/google_sign_in 上找不到方法 init 的实现)