Flutter地理位置获取插件fl_coffee_geolocation的使用
Flutter地理位置获取插件fl_coffee_geolocation的使用
Coffee Flutter Geolocation
[预览]
Coffee Flutter Geolocation 插件为 Flutter 应用程序提供了强大的位置跟踪功能,特别适合后台操作。它非常适合用于健身追踪器、基于位置的服务等场景。
特性
- 后台位置跟踪:在后台持续跟踪用户的位置,确保应用程序即使在未使用时也能了解用户的移动情况。
- 可配置精度:可以选择不同的精度级别以平衡精确度和电池效率。
- 自定义通知:当应用程序在后台跟踪用户位置时,可以使用通知来告知用户。
- 服务器集成:可以无缝地将位置数据发送到指定的服务器URL进行实时跟踪或分析。
- 调试模式:提供调试模式以帮助开发和故障排除。
开始使用
本项目提供了一个 Flutter 插件包的起点,并包含了针对 Android 和 iOS 的平台特定实现代码。它适用于需要高精度用户位置跟踪的应用程序。
前提条件
- Flutter SDK
- Android Studio 或 Visual Studio Code
- 对 Flutter 和 Dart 的基本理解
- Swift、Java
安装
在 pubspec.yaml
文件中添加 fl_coffee_geolocation
作为依赖项:
dependencies:
fl_coffee_geolocation: ^latest_version
Android 设置
确保在 Android 清单文件中包含必要的权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
iOS 设置
确保您的 iOS 项目的 Info.plist
包含以下键及其适当的描述:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>为了增强您的体验,我们需要您的实时位置,即使在应用后台运行时也是如此,以确保您始终处于正确的路径。</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>我们需要您的位置来帮助您导航。</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>fetch</string>
<string>location</string>
</array>
支持
有关如何开始使用 Flutter 开发的帮助,请查看示例目录中的演示。
示例代码
以下是一个完整的示例代码,展示了如何使用 fl_coffee_geolocation
插件获取和显示用户的位置信息。
import 'package:fl_coffee_geolocation/models/config.dart';
import 'package:fl_coffee_geolocation/models/location.dart';
import 'package:fl_coffee_geolocation/models/location_config.dart';
import 'package:fl_coffee_geolocation/models/notification.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:get/get.dart';
import 'package:latlong2/latlong.dart';
import 'package:fl_coffee_geolocation/fl_coffee_geolocation.dart';
import 'dart:math' as math;
import 'location_settings_dialog.dart';
const primaryColor = Color.fromRGBO(2,71,76, 1);
const accentColor = Color.fromRGBO(120,202,78, 1);
const yellowColor = Color.fromRGBO(200,229,31, 1);
void main() {
runApp(const GetMaterialApp(home: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Coffee Background Geolocation Example',
home: CoffeeBackgroudGeolocationScreen(), // 将 LocationMapScreen 设为首页
);
}
}
class CoffeeBackgroudGeolocationScreen extends StatefulWidget {
const CoffeeBackgroudGeolocationScreen({super.key});
[@override](/user/override)
// ignore: library_private_types_in_public_api
_CoffeeBackgroudGeolocationScreenState createState() => _CoffeeBackgroudGeolocationScreenState();
}
class _CoffeeBackgroudGeolocationScreenState extends State<CoffeeBackgroudGeolocationScreen> {
bool _isLoading = true;
bool _isTracking = false;
bool _isInitializing = false;
bool _initSucceeded = false;
bool _isLocationCardExpanded = true;
CoffeeBackgroundLocation? _currentLocation;
final MapController _mapController = MapController();
List<Marker> _currentMarkers = [];
List<Marker> _updatedLocationMarkers = [];
List<Polygon> _geofenceMarkers = [];
List<CoffeeBackgroundLocation> _pathlinePositions = [];
double _distanceFilter = 0;
bool _showFences = true;
[@override](/user/override)
void initState() {
super.initState();
Future.delayed(const Duration(seconds: 2),() {
_getCurrentLocation();
});
}
Future<void> _fetchAndSetDistanceFilter() async {
LocationConfig? locationSettings = await CoffeeBackgroundGeolocation.getLocationSettings();
if (locationSettings != null) {
setState(() {
_distanceFilter = locationSettings.distanceFilter ?? 0;
});
}
}
Future<void> _init() async {
setState(() {
_isInitializing = true;
});
try {
await CoffeeBackgroundGeolocation.init(Config(
location: LocationConfig(
desiredAccuracy: Config.desiredAcuracyNavigation,
distanceFilter: 5
),
debug: true,
notification: CoffeeBackgroundNotification(
priority: Config.notificationPriorityHigh,
title: "Running in background",
text: "This is a message for the notification"
)
));
await _fetchAndSetDistanceFilter();
setState(() {
_initSucceeded = true;
});
} catch (e) {
Get.snackbar(
"Initialization Error",
"Failed to initialize: $e",
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.redAccent,
colorText: Colors.white,
borderRadius: 10,
margin: const EdgeInsets.all(10),
duration: const Duration(seconds: 3),
);
}
setState(() {
_isInitializing = false;
});
}
void _getCurrentLocation() async {
setState(() {
_isLoading = true;
});
try {
CoffeeBackgroundLocation? location = await CoffeeBackgroundGeolocation.getCurrentLocation();
if (location == null) {
setState(() {
_isLoading = false;
});
Get.snackbar(
"Error",
"Failed to get current location",
snackPosition: SnackPosition.BOTTOM,
borderRadius: 10,
margin: const EdgeInsets.all(10),
duration: const Duration(seconds: 2),
);
return;
}
final currentMarker = _buildMarker(
latitude: location.latitude,
longitude: location.longitude,
heading: location.heading,
color: Colors.blue
);
if(_currentLocation == null) {
_centerMap(location, zoom: 15);
}
setState(() {
_currentLocation = location;
_isLoading = false;
_currentMarkers = [currentMarker];
});
} catch (e) {
setState(() {
_isLoading = false;
});
Get.snackbar(
"Error",
"Failed to get location: $e",
snackPosition: SnackPosition.BOTTOM,
borderRadius: 10,
margin: const EdgeInsets.all(10),
duration: const Duration(seconds: 2),
);
}
}
void _toggleTracking() async {
if(!_initSucceeded) {
Get.snackbar(
"Initialization Required",
"Please initialize the Coffee Background Geolocation service first by pressing the 'Initialize Coffee' button.",
snackPosition: SnackPosition.BOTTOM,
borderRadius: 10,
margin: const EdgeInsets.all(10),
duration: const Duration(seconds: 2),
);
return;
}
if (_isTracking) {
await CoffeeBackgroundGeolocation.stop();
} else {
await CoffeeBackgroundGeolocation.start();
CoffeeBackgroundGeolocation.onLocation((CoffeeBackgroundLocation location) {
if(_currentLocation == null) {
_centerMap(location, zoom: 15);
}
_handleLocationUpdate(location);
});
}
setState(() {
_isTracking = !_isTracking;
});
}
void _handleLocationUpdate(CoffeeBackgroundLocation location) {
// 添加新位置到路径线
_pathlinePositions.add(location);
// 添加新的围栏标记
List<LatLng> circlePoints = createCirclePoints(
LatLng(location.latitude, location.longitude),
_distanceFilter
);
Polygon geofenceCircle = Polygon(
points: circlePoints,
color: accentColor.withOpacity(0.5),
borderColor: accentColor,
isFilled: true,
borderStrokeWidth: 2.0,
);
setState(() {
_geofenceMarkers.add(geofenceCircle);
});
// 更新 UI
final updateMarker = _buildMarker(
latitude: location.latitude,
longitude: location.longitude,
heading: location.heading,
color: primaryColor
);
setState(() {
_pathlinePositions = _pathlinePositions;
_geofenceMarkers = _geofenceMarkers;
_updatedLocationMarkers = [updateMarker];
_currentLocation = location;
});
}
Future<void> _openDialogSettings() async {
if(!_initSucceeded) {
Get.snackbar(
"Initialization Required",
"Please initialize the Coffee Background Geolocation service first by pressing the 'Initialize Coffee' button.",
snackPosition: SnackPosition.BOTTOM,
borderRadius: 10,
margin: const EdgeInsets.all(10),
duration: const Duration(seconds: 2),
);
return;
}
final result = await showDialog(
context: context,
builder: (BuildContext context) {
return LocationSettingsDialog(showFences: _showFences);
},
);
if(result != null) {
if(result['resetPathLine'] == true) {
setState(() {
_pathlinePositions = [];
_geofenceMarkers = [];
});
}
setState(() {
_showFences = result['showFences'];
});
_fetchAndSetDistanceFilter();
}
}
void _centerMap(CoffeeBackgroundLocation location, { double? zoom }) {
_mapController.move(LatLng(location.latitude, location.longitude), zoom ?? _mapController.camera.zoom);
}
List<LatLng> createCirclePoints(LatLng center, double radiusInMeters) {
const int circlePointsCount = 60; // 调整以获得所需的多边形密度
List<LatLng> points = [];
for (int i = 0; i < circlePointsCount; i++) {
double angle = (360 / circlePointsCount) * i;
// 将角度和半径转换为弧度
double radians = angle * (math.pi / 180);
double earthRadius = 6378137.0; // 地球半径(米)
double latRadian = center.latitude * (math.pi / 180);
double dx = radiusInMeters * math.cos(radians);
double dy = radiusInMeters * math.sin(radians);
double lat = center.latitude + (dy / earthRadius) * (180 / math.pi);
double lon = center.longitude + (dx / (earthRadius * math.cos(latRadian))) * (180 / math.pi);
points.add(LatLng(lat, lon));
}
return points;
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
'Coffee Background Geolocation',
style: TextStyle(color: yellowColor),
),
backgroundColor: primaryColor,
),
body: SafeArea(
child: FlutterMap(
mapController: _mapController,
options: const MapOptions(
initialCenter: LatLng(0, 0), // 默认中心点
initialZoom: 13.0,
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
subdomains: const ['a', 'b', 'c'],
),
_showFences ?
PolygonLayer(
polygons: _geofenceMarkers,
) : const SizedBox(),
PolylineLayer(polylines: [
Polyline(
points: _pathlinePositions.map((e) => LatLng(e.latitude, e.longitude)).toList(),
strokeWidth: 4.0,
color: Colors.green,
),
]),
MarkerLayer(
markers: _pathlinePositions.map((e) => _buildMarker(
latitude: e.latitude,
longitude: e.longitude,
heading: e.heading,
color: Colors.grey
)).toList(),
),
MarkerLayer(
markers: _isTracking ? _currentMarkers : _updatedLocationMarkers,
),
MarkerLayer(
markers: _isTracking ? _updatedLocationMarkers : _currentMarkers,
),
Stack(
children: [
Positioned(
left: 10,
bottom: 10,
child: _currentLocation != null ? _buildLocationCard() : const SizedBox(),
),
Positioned(
top: 20,
right: 10,
child: Column(
children: [
FloatingActionButton(
onPressed: _getCurrentLocation,
backgroundColor: _isLoading ? Colors.grey : primaryColor,
child: _isLoading ?
const CircularProgressIndicator(color: Colors.white) :
const Icon(Icons.my_location, color: Colors.white,),
),
const SizedBox(height: 20),
FloatingActionButton(
onPressed: _toggleTracking,
backgroundColor: Colors.white,
child: Icon(
_isTracking ? Icons.stop : Icons.play_arrow,
color: _isTracking ? Colors.red : accentColor,
),
),
const SizedBox(height: 20),
FloatingActionButton(
onPressed: _openDialogSettings,
backgroundColor: Colors.white,
child: const Icon(Icons.settings,
color: primaryColor,
),
),
const SizedBox(height: 20),
FloatingActionButton(
onPressed: () {
if(_currentLocation == null) {
return;
}
_centerMap(_currentLocation!);
},
backgroundColor: yellowColor,
child: const Icon(Icons.center_focus_strong,
color: primaryColor,
),
),
],
)
),
Positioned(
top: 10,
left: 10,
child: _buildInitButton()
)
]
)
],
),
)
);
}
Widget _buildInitButton() {
if(_isInitializing) {
return const CircularProgressIndicator(
color: primaryColor,
);
}
if(_initSucceeded) {
return ElevatedButton(
onPressed: null,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
disabledForegroundColor: Colors.green,
),
child: const Text(
'Init Succeeded',
style: TextStyle(
color: primaryColor
),
),
);
}
return ElevatedButton(
onPressed: () {
_init();
},
style: ElevatedButton.styleFrom(
backgroundColor: accentColor,
),
child: const Text(
'Initialize Coffee',
style: TextStyle(
color: Colors.white
),
),
);
}
Marker _buildMarker({
required double latitude,
required double longitude,
required double heading,
Color color = primaryColor
}) {
return Marker(
width: 20.0,
height: 20.0,
point: LatLng(latitude, longitude),
child: Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.8),
shape: BoxShape.circle,
border: Border.all(color: color, width: 2),
),
child: Transform.rotate(
angle: heading * (math.pi / 180),
child: Icon(Icons.navigation, color: color, size: 14),
),
),
);
}
Widget _buildLocationCard() {
String title = "Current Location";
if(_isTracking && _updatedLocationMarkers.isNotEmpty) {
title = "Tracking Location";
}
if(_currentLocation == null) {
return const SizedBox();
}
return Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.6),
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(8), // 设置所需的内边距
color: Colors.black.withOpacity(0.6), // 设置背景颜色
child: GestureDetector(
onTap: () {
setState(() {
_isLocationCardExpanded = !_isLocationCardExpanded;
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
Icon(
_isLocationCardExpanded ? Icons.expand_less : Icons.expand_more,
color: Colors.white,
)
],
),
)
),
SizedBox(height: _isLocationCardExpanded ? 10 : 0),
if (_isLocationCardExpanded)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_locationDetailRow('Latitude', _currentLocation!.latitude.toString()),
_locationDetailRow('Longitude', _currentLocation!.longitude.toString()),
_locationDetailRow('Accuracy', _currentLocation!.accuracy.toString()),
_locationDetailRow('Altitude', _currentLocation!.altitude.toString()),
_locationDetailRow('Heading', _currentLocation!.heading.toString()),
_locationDetailRow('Speed', _currentLocation!.speed.toString()),
_locationDetailRow('SpeedAccuracy', _currentLocation!.speedAccuracy.toString()),
_locationDetailRow('BatteryLevel', _currentLocation!.batteryLevel.toString()),
_locationDetailRow('DeviceVersion', _currentLocation!.deviceVersion.toString()),
_locationDetailRow('DeviceName', _currentLocation!.deviceName.toString()),
_locationDetailRow('DeviceType', _currentLocation!.deviceType.toString()),
_locationDetailRow('DateTime', _currentLocation!.dateTime.toString()),
_locationDetailRow('ConnectionType', _currentLocation!.internetConnectionType.toString())
],
),
]
)
);
}
Widget _locationDetailRow(String property, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Row(
children: [
Text('$property: ', style: const TextStyle(color: Colors.white)),
Text(value, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
],
),
);
}
}
更多关于Flutter地理位置获取插件fl_coffee_geolocation的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter地理位置获取插件fl_coffee_geolocation的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
fl_coffee_geolocation
是一个用于在 Flutter 应用中获取设备地理位置的插件。它提供了简单易用的 API,可以帮助你轻松地获取设备的地理位置信息。以下是使用 fl_coffee_geolocation
插件的基本步骤:
1. 添加依赖
首先,你需要在 pubspec.yaml
文件中添加 fl_coffee_geolocation
插件的依赖:
dependencies:
flutter:
sdk: flutter
fl_coffee_geolocation: ^1.0.0 # 请根据实际情况使用最新版本
然后,运行 flutter pub get
来安装依赖。
2. 导入插件
在你的 Dart 文件中导入 fl_coffee_geolocation
插件:
import 'package:fl_coffee_geolocation/fl_coffee_geolocation.dart';
3. 获取地理位置
使用 FlCoffeeGeolocation
类来获取设备的地理位置信息。你可以使用 getCurrentPosition
方法来获取当前的经纬度。
void getLocation() async {
try {
Position position = await FlCoffeeGeolocation.getCurrentPosition();
print("Latitude: ${position.latitude}");
print("Longitude: ${position.longitude}");
} catch (e) {
print("Error: $e");
}
}
4. 处理权限
在 Android 和 iOS 上,获取地理位置需要用户授权。你需要在 AndroidManifest.xml
和 Info.plist
文件中添加相应的权限声明。
Android
在 android/app/src/main/AndroidManifest.xml
中添加以下权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
iOS
在 ios/Runner/Info.plist
中添加以下键值对:
<key>NSLocationWhenInUseUsageDescription</key>
<string>We need access to your location to provide location-based services.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>We need access to your location to provide location-based services.</string>
5. 请求权限
在应用启动时,你可以请求用户授予地理位置权限:
void requestPermission() async {
PermissionStatus permissionStatus = await FlCoffeeGeolocation.requestPermission();
if (permissionStatus == PermissionStatus.granted) {
print("Permission granted");
} else {
print("Permission denied");
}
}
6. 持续监听位置变化
如果你需要持续监听设备的位置变化,可以使用 getPositionStream
方法:
void listenLocation() {
StreamSubscription<Position> positionStream = FlCoffeeGeolocation.getPositionStream().listen((Position position) {
print("Updated Latitude: ${position.latitude}");
print("Updated Longitude: ${position.longitude}");
});
// 当你不再需要监听位置变化时,记得取消订阅
positionStream.cancel();
}
7. 处理错误
在获取地理位置时,可能会遇到各种错误,例如权限被拒绝、定位服务未启用等。你可以通过捕获异常来处理这些错误:
void getLocation() async {
try {
Position position = await FlCoffeeGeolocation.getCurrentPosition();
print("Latitude: ${position.latitude}");
print("Longitude: ${position.longitude}");
} on PermissionDeniedException catch (e) {
print("Permission denied: $e");
} on LocationServiceDisabledException catch (e) {
print("Location service disabled: $e");
} catch (e) {
print("Error: $e");
}
}
8. 其他功能
fl_coffee_geolocation
还提供了其他功能,例如获取最后一次已知的位置、检查定位服务是否启用等。你可以参考插件的文档来了解更多细节。
9. 示例代码
以下是一个完整的示例代码,展示了如何使用 fl_coffee_geolocation
获取设备的地理位置并显示在屏幕上:
import 'package:flutter/material.dart';
import 'package:fl_coffee_geolocation/fl_coffee_geolocation.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: LocationScreen(),
);
}
}
class LocationScreen extends StatefulWidget {
[@override](/user/override)
_LocationScreenState createState() => _LocationScreenState();
}
class _LocationScreenState extends State<LocationScreen> {
String _location = "Unknown";
void _getLocation() async {
try {
Position position = await FlCoffeeGeolocation.getCurrentPosition();
setState(() {
_location = "Latitude: ${position.latitude}, Longitude: ${position.longitude}";
});
} catch (e) {
setState(() {
_location = "Error: $e";
});
}
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Location Example"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_location),
ElevatedButton(
onPressed: _getLocation,
child: Text("Get Location"),
),
],
),
),
);
}
}