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 上完美运行