Flutter Geolocator 和 Google Maps:显示静态位置

Posted

技术标签:

【中文标题】Flutter Geolocator 和 Google Maps:显示静态位置【英文标题】:Flutter Geolocator and Google Maps: Show static location 【发布时间】:2021-10-08 11:15:39 【问题描述】:

我在执行以下实现时遇到了问题:

当用户授予位置权限时,我的 Flutter 应用程序运行良好,但当用户拒绝权限时,我遇到了麻烦。加载谷歌地图时显示加载指示器,但当用户拒绝权限时,它会继续旋转。我想实现一条错误消息,显示需要权限,但我不知道如何。

代码下方:

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'dart:typed_data';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geolocator/geolocator.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:hvd_test/styling/colors.dart';
import 'package:hvd_test/models/navigation.dart';
import 'package:hvd_test/models/markers.dart';

// This page shows a Google Map plugin with all stations (HvD and Total). The markers are pulled from a Firebase database.

class StationsMap extends StatefulWidget 
  @override
  _StationsMap createState() => _StationsMap();


class _StationsMap extends State<StationsMap> 
  
  bool mapToggle = false;

  var currentLocation;

  GoogleMapController mapController;

  Map<MarkerId, Marker> markers = <MarkerId, Marker>;

// Below function initiates all HvD stations and shows them as markers on the map. It also generates a Bottom Sheet for each location with corresponding information. 

  void initMarkerHvD(specify, specifyId) async 
    var markerIdVal = specifyId;
    final Uint8List markerHvD = await getBytesFromAsset('images/Pin-HvD.JPG', 70);
    final MarkerId markerId = MarkerId(markerIdVal);
    final Marker marker = Marker(
      markerId: markerId,
      onTap: () 
        showModalBottomSheet(
            context: context,
            builder: (context) => SingleChildScrollView(
                  child: Container(
                    padding: EdgeInsets.only(
                        bottom: MediaQuery.of(context).viewInsets.bottom),
                    child: Container(
                      color: Color(0xff757575),
                      child: Container(
                        padding: EdgeInsets.all(20.0),
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.only(
                            topLeft: const Radius.circular(20.0),
                            topRight: const Radius.circular(20.0)
                          )
                        ),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.stretch,
                          children: [
                            Text(
                              specify['stationName'], style: TextStyle(color: PaletteBlue.hvdblue, fontWeight: FontWeight.bold, fontSize: 16), textAlign: TextAlign.center,
                            ),
                            SizedBox(height: 10),
                            Text(
                              specify['stationAddress']
                            ),
                            Text(
                              specify['stationZIP'] + ' ' + specify['stationCity']
                            ),
                            SizedBox(height: 20),
                            ElevatedButton(
                                child: Text(
                                  'Navigeer naar locatie',
                                  style: TextStyle(
                                    color: Colors.white,
                                  ),
                                ),
                                style: ButtonStyle(
                                    backgroundColor: MaterialStateProperty.all(
                                        PaletteOrange.hvdorange)),
                                onPressed: () 
                                  MapUtils.openMap(
                                      specify['stationLocation'].latitude,
                                      specify['stationLocation'].longitude);
                                ),
                          ],
                        ),
                      ),
                    ),
                  ),
                ));
      ,
      position: LatLng(specify['stationLocation'].latitude,
          specify['stationLocation'].longitude),
      infoWindow: InfoWindow(),
      icon: BitmapDescriptor.fromBytes(markerHvD),
    );
    setState(() 
      markers[markerId] = marker;
    );
  

// Below function initiates all Total stations and shows them as markers on the map. It also generates a Bottom Sheet for each location with corresponding information. 

  void initMarkerTotal(specify, specifyId) async 
    var markerIdVal = specifyId;
    final Uint8List markerTotal = await getBytesFromAsset('images/Pin-Total.JPG', 70);
    final MarkerId markerId = MarkerId(markerIdVal);
    final Marker marker = Marker(
      markerId: markerId,
      onTap: () 
        showModalBottomSheet(
            context: context,
            builder: (context) => SingleChildScrollView(
                  child: Container(
                    padding: EdgeInsets.only(
                        bottom: MediaQuery.of(context).viewInsets.bottom),
                    child: Container(
                      color: Color(0xff757575),
                      child: Container(
                        padding: EdgeInsets.all(20.0),
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.only(
                            topLeft: const Radius.circular(20.0),
                            topRight: const Radius.circular(20.0)
                          )
                        ),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.stretch,
                          children: [
                            Text(
                              specify['stationName'], style: TextStyle(color: PaletteBlue.hvdblue, fontWeight: FontWeight.bold, fontSize: 16), textAlign: TextAlign.center,
                            ),
                            SizedBox(height: 10),
                            Text(
                              specify['stationAddress']
                            ),
                            Text(
                              specify['stationZIP'] + ' ' + specify['stationCity']
                            ),
                            SizedBox(height: 20),
                            ElevatedButton(
                                child: Text(
                                  'Navigeer naar locatie',
                                  style: TextStyle(
                                    color: Colors.white,
                                  ),
                                ),
                                style: ButtonStyle(
                                    backgroundColor: MaterialStateProperty.all(
                                        PaletteOrange.hvdorange)),
                                onPressed: () 
                                  MapUtils.openMap(
                                      specify['stationLocation'].latitude,
                                      specify['stationLocation'].longitude);
                                ),
                          ],
                        ),
                      ),
                    ),
                  ),
                ));
      ,
      position: LatLng(specify['stationLocation'].latitude,
          specify['stationLocation'].longitude),
      infoWindow: InfoWindow(),
      icon: BitmapDescriptor.fromBytes(markerTotal),
    );
    setState(() 
      markers[markerId] = marker;
    );
  

// Below functions pulls all HvD markers from the database. 

  getMarkerDataHvD() async 
    FirebaseFirestore.instance
        .collection('hvd-stations')
        .get()
        .then((myMarkers) 
      if (myMarkers.docs.isNotEmpty) 
        for (int i = 0; i < myMarkers.docs.length; i++) 
          initMarkerHvD(myMarkers.docs[i].data(), myMarkers.docs[i].id);
        
      
    );
  

// Below function pulls all Total markers from the database. 

  getMarkerDataTotal() async 
    FirebaseFirestore.instance
        .collection('total-stations')
        .get()
        .then((myMarkers) 
      if (myMarkers.docs.isNotEmpty) 
        for (int i = 0; i < myMarkers.docs.length; i++) 
          initMarkerTotal(myMarkers.docs[i].data(), myMarkers.docs[i].id);
        
      
    );
  

// Below function initiates all previous functions on the page. This happens when the user navigates to the page. 

  void initState() 
    getMarkerDataHvD();
    getMarkerDataTotal();
    super.initState();
    Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high,)
        .then((currloc) 
      setState(() 
        currentLocation = currloc;
        mapToggle = true;
      );
    );
  

// Below function is used to display all previous functions to the page. 

  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      home: Scaffold(
        body: Stack(
          children: <Widget>[
            mapToggle
                ? GoogleMap(
                    onMapCreated: onMapCreated,
                    markers: Set<Marker>.of(markers.values),
                    gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>[
                      new Factory<OneSequenceGestureRecognizer>(
                        () => new EagerGestureRecognizer(),
                      ),
                    ].toSet(),
                    mapToolbarEnabled: false,
                    zoomGesturesEnabled: true,
                    zoomControlsEnabled: false,
                    scrollGesturesEnabled: true,
                    myLocationEnabled: true,
                    myLocationButtonEnabled: true,
                    initialCameraPosition: CameraPosition(
                      target: LatLng(
                          currentLocation.latitude, currentLocation.longitude),
                      zoom: 12.0,
                    ),
                  )
                : Center(
                    child: CircularProgressIndicator(
                      color: PaletteOrange.hvdorange,
                    ),
                  ),
          ],
        ),
      ),
    );
  

  void onMapCreated(controller) 
    setState(() 
      mapController = controller;
    );
  

非常感谢任何帮助!

2021 年 4 月 8 日更新:

所以我更改了代码以始终在静态点上显示地图。当用户不授予权限时,他们可以在没有位置的情况下使用地图。

但是当用户允许该位置时,地图会自动移动到地图上的位置...

我现在遇到的问题是,当用户授予权限时,相机不会移动到用户位置,当我授予权限然后使用 MyLocation 按钮时它可以工作,但我希望它是自动的……它正在驾驶我疯了!

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'dart:typed_data';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geolocator/geolocator.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:hvd_test/styling/colors.dart';
import 'package:hvd_test/models/navigation.dart';
import 'package:hvd_test/models/markers.dart';

// This page shows a Google Map plugin with all stations (HvD and Total). The markers are pulled from a Firebase database.

class StationsMap extends StatefulWidget 
  @override
  _StationsMap createState() => _StationsMap();


class _StationsMap extends State<StationsMap> 

  bool _isLocationGranted = false;

  var currentLocation;

  GoogleMapController mapController;

  Map<MarkerId, Marker> markers = <MarkerId, Marker>;

// Below function initiates all HvD stations and shows them as markers on the map. It also generates a Bottom Sheet for each location with corresponding information. 

  void initMarkerHvD(specify, specifyId) async 
    var markerIdVal = specifyId;
    final Uint8List markerHvD = await getBytesFromAsset('images/Pin-HvD.JPG', 70);
    final MarkerId markerId = MarkerId(markerIdVal);
    final Marker marker = Marker(
      markerId: markerId,
      onTap: () 
        showModalBottomSheet(
            context: context,
            builder: (context) => SingleChildScrollView(
                  child: Container(
                    padding: EdgeInsets.only(
                        bottom: MediaQuery.of(context).viewInsets.bottom),
                    child: Container(
                      color: Color(0xff757575),
                      child: Container(
                        padding: EdgeInsets.all(20.0),
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.only(
                            topLeft: const Radius.circular(20.0),
                            topRight: const Radius.circular(20.0)
                          )
                        ),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.stretch,
                          children: [
                            Text(
                              specify['stationName'], style: TextStyle(color: PaletteBlue.hvdblue, fontWeight: FontWeight.bold, fontSize: 16), textAlign: TextAlign.center,
                            ),
                            SizedBox(height: 10),
                            Text(
                              specify['stationAddress']
                            ),
                            Text(
                              specify['stationZIP'] + ' ' + specify['stationCity']
                            ),
                            SizedBox(height: 20),
                            ElevatedButton(
                                child: Text(
                                  'Navigeer naar locatie',
                                  style: TextStyle(
                                    color: Colors.white,
                                  ),
                                ),
                                style: ButtonStyle(
                                    backgroundColor: MaterialStateProperty.all(
                                        PaletteOrange.hvdorange)),
                                onPressed: () 
                                  MapUtils.openMap(
                                      specify['stationLocation'].latitude,
                                      specify['stationLocation'].longitude);
                                ),
                          ],
                        ),
                      ),
                    ),
                  ),
                ));
      ,
      position: LatLng(specify['stationLocation'].latitude,
          specify['stationLocation'].longitude),
      infoWindow: InfoWindow(),
      icon: BitmapDescriptor.fromBytes(markerHvD),
    );
    setState(() 
      markers[markerId] = marker;
    );
  

// Below function initiates all Total stations and shows them as markers on the map. It also generates a Bottom Sheet for each location with corresponding information. 

  void initMarkerTotal(specify, specifyId) async 
    var markerIdVal = specifyId;
    final Uint8List markerTotal = await getBytesFromAsset('images/Pin-Total.JPG', 70);
    final MarkerId markerId = MarkerId(markerIdVal);
    final Marker marker = Marker(
      markerId: markerId,
      onTap: () 
        showModalBottomSheet(
            context: context,
            builder: (context) => SingleChildScrollView(
                  child: Container(
                    padding: EdgeInsets.only(
                        bottom: MediaQuery.of(context).viewInsets.bottom),
                    child: Container(
                      color: Color(0xff757575),
                      child: Container(
                        padding: EdgeInsets.all(20.0),
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.only(
                            topLeft: const Radius.circular(20.0),
                            topRight: const Radius.circular(20.0)
                          )
                        ),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.stretch,
                          children: [
                            Text(
                              specify['stationName'], style: TextStyle(color: PaletteBlue.hvdblue, fontWeight: FontWeight.bold, fontSize: 16), textAlign: TextAlign.center,
                            ),
                            SizedBox(height: 10),
                            Text(
                              specify['stationAddress']
                            ),
                            Text(
                              specify['stationZIP'] + ' ' + specify['stationCity']
                            ),
                            SizedBox(height: 20),
                            ElevatedButton(
                                child: Text(
                                  'Navigeer naar locatie',
                                  style: TextStyle(
                                    color: Colors.white,
                                  ),
                                ),
                                style: ButtonStyle(
                                    backgroundColor: MaterialStateProperty.all(
                                        PaletteOrange.hvdorange)),
                                onPressed: () 
                                  MapUtils.openMap(
                                      specify['stationLocation'].latitude,
                                      specify['stationLocation'].longitude);
                                ),
                          ],
                        ),
                      ),
                    ),
                  ),
                ));
      ,
      position: LatLng(specify['stationLocation'].latitude,
          specify['stationLocation'].longitude),
      infoWindow: InfoWindow(),
      icon: BitmapDescriptor.fromBytes(markerTotal),
    );
    setState(() 
      markers[markerId] = marker;
    );
  

// Below functions pulls all HvD markers from the database. 

  getMarkerDataHvD() async 
    FirebaseFirestore.instance
        .collection('hvd-stations')
        .get()
        .then((myMarkers) 
      if (myMarkers.docs.isNotEmpty) 
        for (int i = 0; i < myMarkers.docs.length; i++) 
          initMarkerHvD(myMarkers.docs[i].data(), myMarkers.docs[i].id);
        
      
    );
  

// Below function pulls all Total markers from the database. 

  getMarkerDataTotal() async 
    FirebaseFirestore.instance
        .collection('total-stations')
        .get()
        .then((myMarkers) 
      if (myMarkers.docs.isNotEmpty) 
        for (int i = 0; i < myMarkers.docs.length; i++) 
          initMarkerTotal(myMarkers.docs[i].data(), myMarkers.docs[i].id);
        
      
    );
  

// Below function initiates all previous functions on the page. This happens when the user navigates to the page. 

  void initState() 
    getMarkerDataHvD();
    getMarkerDataTotal();
    super.initState();
    Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high)
        .then((currloc) 
      setState(() 
        currentLocation = currloc;
        _isLocationGranted = true;
      );
    );
  

// Below function is used to display all previous functions to the page. 

  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      home: Scaffold(
        body: Stack(
          children: <Widget>[
            GoogleMap(
                    onMapCreated: onMapCreated,
                    markers: Set<Marker>.of(markers.values),
                    gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>[
                      new Factory<OneSequenceGestureRecognizer>(
                        () => new EagerGestureRecognizer(),
                      ),
                    ].toSet(),
                    mapToolbarEnabled: false,
                    zoomGesturesEnabled: true,
                    zoomControlsEnabled: false,
                    scrollGesturesEnabled: true,
                    myLocationEnabled: _isLocationGranted,
                    myLocationButtonEnabled: true,
                    initialCameraPosition: CameraPosition(
                      target: LatLng(51.8876176, 5.4278765),
                      zoom: 12.0,
                    ),
                  )
          ],
        ),
      ),
    );
  

  void onMapCreated(controller) 
      mapController = controller;
      mapController.moveCamera(CameraUpdate.newCameraPosition(currentLocation));
  

【问题讨论】:

在geolocator包中,您可以选择检查用户是否接受权限以及用户是否启用定位服务。检查使用情况下会有示例如何使用它 我是 Flutter 的新手,我确实检查了文档并看到了该功能,但我不知道如何在上面的代码中实现它。 另请参考此代码,它曾用于我的项目之一***.com/a/64403196/13418165@EnricovanDijkhuizen 【参考方案1】:

好的,你可以使用这个包permission_handler来检查用户是拒绝还是接受。易于使用的软件包。

注意:不要忘记在Podfile 文件中添加您将要处理的权限。如果您不将此编辑添加到podfile,当您尝试上传到 AppStore 时,您将面临权限问题。您应该阅读文档。

包链接:https://pub.dev/packages/permission_handler

【讨论】:

查看我上面的评论。在 Geolocator 包中已经有一个检查权限的函数,但我不知道如何将它实现到上面显示的代码中。【参考方案2】:

检查这个代码,我没有测试,试试这个告诉我

import 'package:geolocator/geolocator.dart';

/// Determine the current position of the device.
///
/// When the location services are not enabled or permissions
/// are denied the `Future` will return an error.
Future<Position> _determinePosition() async 
  bool serviceEnabled;
  LocationPermission permission;

  // Test if location services are enabled.
  serviceEnabled = await Geolocator.isLocationServiceEnabled();
  if (!serviceEnabled) 
    // Location services are not enabled don't continue
    // accessing the position and request users of the 
    // App to enable the location services.
//instead this add a snackbar 
    return Future.error('Location services are disabled.');
  

  permission = await Geolocator.checkPermission();
  if (permission == LocationPermission.denied) 
    permission = await Geolocator.requestPermission();
    if (permission == LocationPermission.denied) 
      // Permissions are denied, next time you could try
      // requesting permissions again (this is also where
      // android's shouldShowRequestPermissionRationale 
      // returned true. According to Android guidelines
      // your App should show an explanatory UI now.
//instead this add a snackbar 
      return Future.error('Location permissions are denied');
    
  
  
  if (permission == LocationPermission.deniedForever) 
    // Permissions are denied forever, handle appropriately. 
//instead this add a snackbar 
    return Future.error(
      'Location permissions are permanently denied, we cannot request permissions.');
   

  // When we reach here, permissions are granted and we can
  // continue accessing the position of the device.
  return await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high,)
        .then((currloc) 
      setState(() 
        currentLocation = currloc;
        mapToggle = true;
      print("LAT: $currentLocation .latitude, LNG: $currentLocation .longitude");
      );
    );

在initState中调用上述方法

void initState() 
    getMarkerDataHvD();
    getMarkerDataTotal();
    super.initState();
    _determinePosition();
  

【讨论】:

感谢您的帮助!我已经尝试过了,但是当我实现它时它不起作用。我保留了 CircularProgressIndicator 并且在应用程序中没有收到错误消息。我认为这与mapToggle和CircularProgressIndicator的实现有关,但我不知道如何调整它。 你可以试试这个 mapToggle!=true 吗? GoogleMap():Center(child: CircularProgressIndicator(color: PaletteOrange.hvdorange, ), ), @EnricovanDijkhuizen 再次感谢您的帮助。当我这样做时,我收到以下错误消息: getter 'latitude' was called on null。 Receiver: null 尝试调用: latitude 导致错误的相关小部件是 StationsMap lib\pages\stations.dart:46 看起来纬度是空的,我已经更新了代码,再试一次,有纬度和经度的打印,告诉我输出@EnricovanDijkhuizen 我运行了你的代码并得到了这个:`════════ 小部件库捕获的异常═══════════════════ ════════════════ getter 'latitude' 在 null 上调用。 Receiver: null 尝试调用:latitude 导致错误的相关小部件是 StationsMap lib\pages\stations.dart:46 ══════════════════════════ ══════════════════════════════════════════════════ ════ I/颤振(5739):LAT:51.8876167,LNG:5.427875 `

以上是关于Flutter Geolocator 和 Google Maps:显示静态位置的主要内容,如果未能解决你的问题,请参考以下文章

未找到 Flutter 插件“geolocator-Swift.h”文件

Flutter Geolocator 和 Google Maps:显示静态位置

Flutter Geolocator 显示方法 toDouble() 在 null 上被调用

Flutter Geolocator 无法在 Android 上运行,但可以在 iOS 上完美运行

Flutter:Geolocator返回方法'compareTo'在null上被调用

Flutter geolocator 包在 IOS 应用程序上给出负纬度,在 Android 上给出正确坐标