Flutter后台定位插件background_locator的使用
Flutter后台定位插件background_locator的使用
插件简介
background_locator
是一个Flutter插件,允许在应用程序被杀死的情况下仍能接收位置更新。该插件可以满足开发者对于应用在后台或完全关闭后继续获取用户地理位置的需求,例如用于记录运动轨迹、地理围栏等功能。
更多详情请参考 Wiki页面,其中包括但不限于:
License
本项目采用MIT许可证,详细内容见LICENSE文件。
Contributor
感谢所有为此插件做出贡献的人,包括但不限于:
示例代码
下面是一个完整的示例demo,演示了如何使用background_locator
插件来实现后台定位功能。
import 'dart:async';
import 'dart:isolate';
import 'dart:ui';
import 'package:background_locator/background_locator.dart';
import 'package:background_locator/location_dto.dart';
import 'package:background_locator/settings/android_settings.dart';
import 'package:background_locator/settings/ios_settings.dart';
import 'package:background_locator/settings/locator_settings.dart';
import 'package:flutter/material.dart';
import 'package:location_permissions/location_permissions.dart';
import 'file_manager.dart';
import 'location_callback_handler.dart';
import 'location_service_repository.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ReceivePort port = ReceivePort();
String logStr = '';
bool isRunning;
LocationDto lastLocation;
@override
void initState() {
super.initState();
if (IsolateNameServer.lookupPortByName(LocationServiceRepository.isolateName) != null) {
IsolateNameServer.removePortNameMapping(LocationServiceRepository.isolateName);
}
IsolateNameServer.registerPortWithName(port.sendPort, LocationServiceRepository.isolateName);
port.listen(
(dynamic data) async {
await updateUI(data);
},
);
initPlatformState();
}
@override
void dispose() {
super.dispose();
}
Future<void> updateUI(LocationDto data) async {
final log = await FileManager.readLogFile();
await _updateNotificationText(data);
setState(() {
if (data != null) {
lastLocation = data;
}
logStr = log;
});
}
Future<void> _updateNotificationText(LocationDto data) async {
if (data == null) {
return;
}
await BackgroundLocator.updateNotificationText(
title: "new location received",
msg: "${DateTime.now()}",
bigMsg: "${data.latitude}, ${data.longitude}");
}
Future<void> initPlatformState() async {
print('Initializing...');
await BackgroundLocator.initialize();
logStr = await FileManager.readLogFile();
print('Initialization done');
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
});
print('Running ${isRunning.toString()}');
}
@override
Widget build(BuildContext context) {
final start = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Start'),
onPressed: () {
_onStart();
},
),
);
final stop = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Stop'),
onPressed: () {
onStop();
},
),
);
final clear = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Clear Log'),
onPressed: () {
FileManager.clearLogFile();
setState(() {
logStr = '';
});
},
),
);
String msgStatus = "-";
if (isRunning != null) {
if (isRunning) {
msgStatus = 'Is running';
} else {
msgStatus = 'Is not running';
}
}
final status = Text("Status: $msgStatus");
final log = Text(
logStr,
);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter background Locator'),
),
body: Container(
width: double.maxFinite,
padding: const EdgeInsets.all(22),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[start, stop, clear, status, log],
),
),
),
),
);
}
void onStop() async {
await BackgroundLocator.unRegisterLocationUpdate();
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
});
}
void _onStart() async {
if (await _checkLocationPermission()) {
await _startLocator();
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
lastLocation = null;
});
} else {
// show error
}
}
Future<bool> _checkLocationPermission() async {
final access = await LocationPermissions().checkPermissionStatus();
switch (access) {
case PermissionStatus.unknown:
case PermissionStatus.denied:
case PermissionStatus.restricted:
final permission = await LocationPermissions().requestPermissions(
permissionLevel: LocationPermissionLevel.locationAlways,
);
if (permission == PermissionStatus.granted) {
return true;
} else {
return false;
}
break;
case PermissionStatus.granted:
return true;
break;
default:
return false;
break;
}
}
Future<void> _startLocator() async{
Map<String, dynamic> data = {'countInit': 1};
return await BackgroundLocator.registerLocationUpdate(LocationCallbackHandler.callback,
initCallback: LocationCallbackHandler.initCallback,
initDataCallback: data,
disposeCallback: LocationCallbackHandler.disposeCallback,
iosSettings: IOSSettings(
accuracy: LocationAccuracy.NAVIGATION, distanceFilter: 0),
autoStop: false,
androidSettings: AndroidSettings(
accuracy: LocationAccuracy.NAVIGATION,
interval: 5,
distanceFilter: 0,
client: LocationClient.google,
androidNotificationSettings: AndroidNotificationSettings(
notificationChannelName: 'Location tracking',
notificationTitle: 'Start Location Tracking',
notificationMsg: 'Track location in background',
notificationBigMsg:
'Background location is on to keep the app up-tp-date with your location. This is required for main features to work properly when the app is not running.',
notificationIconColor: Colors.grey,
notificationTapCallback:
LocationCallbackHandler.notificationCallback)));
}
}
代码说明
ReceivePort
和IsolateNameServer
用于处理与后台服务之间的通信。updateUI
方法负责更新UI以反映最新的位置信息。_updateNotificationText
方法用于更新通知文本。initPlatformState
方法初始化平台状态并检查服务是否正在运行。_onStart
和onStop
方法分别用于启动和停止位置更新服务。_checkLocationPermission
方法确保应用具有必要的权限来访问位置数据。_startLocator
方法配置并注册位置更新服务,包括iOS和Android的具体设置。
以上就是关于background_locator
插件的详细介绍及使用示例。希望对您有所帮助!如果有任何问题或者需要进一步的帮助,请随时提问。
更多关于Flutter后台定位插件background_locator的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter后台定位插件background_locator的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter应用中使用background_locator
插件来实现后台定位的示例代码。这个插件允许应用在后台持续获取用户的位置信息。
首先,确保你已经在pubspec.yaml
文件中添加了background_locator
和geolocator
依赖项:
dependencies:
flutter:
sdk: flutter
geolocator: ^9.0.2 # 请检查最新版本
background_locator: ^2.0.0 # 请检查最新版本
然后,运行flutter pub get
来安装这些依赖项。
接下来,按照以下步骤配置和使用background_locator
插件:
1. 配置iOS和Android权限
iOS
在ios/Runner/Info.plist
文件中添加以下权限声明:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>需要后台定位权限</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>需要后台定位权限</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要定位权限</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
Android
在android/app/src/main/AndroidManifest.xml
文件中添加以下权限声明:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
同时,在android/app/src/main/kotlin/[your_package_name]/MainActivity.kt
(或者MainActivity.java
,如果你使用的是Java)中添加以下代码以处理权限请求(对于Kotlin):
package com.example.yourappname
import io.flutter.embedding.android.FlutterActivity
import android.os.Bundle
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import android.Manifest
class MainActivity: FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 请求后台定位权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), 1)
}
}
}
2. 初始化Background Locator
在你的Flutter应用中,初始化background_locator
。在lib
目录下创建一个新的Dart文件,比如location_service.dart
,并添加以下代码:
import 'dart:async';
import 'package:background_locator/background_locator.dart';
import 'package:geolocator/geolocator.dart';
class LocationService {
BackgroundLocatorCallback _callback;
LocationCallback _locationCallback;
LocationService._() {
_locationCallback = (Position position) async {
// 在这里处理位置更新,比如保存到数据库
print('Latitude: ${position.latitude}, Longitude: ${position.longitude}');
};
_callback = BackgroundLocator.Callback(
onLocationChanged: _locationCallback,
onPermissionDenied: () {
print('[BackgroundLocator] Permission denied');
},
onServiceDisabled: () {
print('[BackgroundLocator] Service disabled');
},
);
BackgroundLocator.registerLocationUpdate(_callback).then((_) {
print('[BackgroundLocator] BackgroundLocator is configured successfully');
}).catchError((onError) {
print('[BackgroundLocator] FAILED to configure BackgroundLocator: $onError');
});
}
static final LocationService _singleton = LocationService._();
factory LocationService() => _singleton;
Future<void> startLocationService() async {
// 请求权限
bool serviceEnabled;
LocationPermission permission;
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
return Future.error('Location services are disabled.');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
return Future.error('Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
// 处理永久拒绝的情况
return Future.error('Location permissions are permanently denied, we cannot request permissions.');
}
// 开始后台定位服务
await BackgroundLocator.startLocationUpdates();
}
}
3. 使用LocationService
在你的主应用文件(比如main.dart
)中,使用LocationService
来启动后台定位服务:
import 'package:flutter/material.dart';
import 'location_service.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
LocationService().startLocationService().then((_) {
print('Location service started');
}).catchError((onError) {
print('Error starting location service: $onError');
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Background Locator Example'),
),
body: Center(
child: Text('Check the console for location updates'),
),
),
);
}
}
这样,你的Flutter应用就配置好了后台定位服务。当应用在后台运行时,它将持续获取用户的位置信息,并在控制台中打印出来。
请注意,实际应用中你可能需要处理更多的边缘情况和用户交互,比如处理权限请求的结果、在UI中显示位置信息、管理后台任务的生命周期等。此外,后台定位会消耗设备的电池和数据,因此确保你的应用有合理的后台定位策略,以优化用户体验和节省资源。