Flutter后台定位插件background_location_tracker的使用
Flutter后台定位插件background_location_tracker的使用
background_location_tracker
是一个允许你在Android和iOS平台上进行后台位置跟踪的新Flutter插件。以下是如何配置和使用的详细指南。
Android配置
更新编译SDK
确保你的compileSdkVersion
和targetSdkVersion
至少为29:
android {
...
compileSdkVersion 29
...
defaultConfig {
...
targetSdkVersion 29
...
}
...
}
iOS配置
更新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>
更新AppDelegate
确保调用setPluginRegistrantCallback
以使其他插件在后台可用:
import UIKit
import Flutter
import background_location_tracker
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
GeneratedPluginRegistrant.register(with: self)
BackgroundLocationTrackerPlugin.setPluginRegistrantCallback { registry in
GeneratedPluginRegistrant.register(with: registry)
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Flutter实现
确保设置@pragma('vm:entry-point')
以便在发布模式下找到回调函数:
@pragma('vm:entry-point')
void backgroundCallback() {
BackgroundLocationTrackerManager.handleBackgroundUpdated(
(data) async => Repo().update(data),
);
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await BackgroundLocationTrackerManager.initialize(
backgroundCallback,
config: const BackgroundLocationTrackerConfig(
loggingEnabled: true,
androidConfig: AndroidConfig(
notificationIcon: 'explore',
trackingInterval: Duration(seconds: 4),
distanceFilterMeters: null,
),
iOSConfig: IOSConfig(
activityType: ActivityType.FITNESS,
distanceFilterMeters: null,
restartAfterKill: true,
),
),
);
runApp(MyApp());
}
Future startLocationTracking() async {
await BackgroundLocationTrackerManager.startTracking();
}
Future stopLocationTracking() async {
await BackgroundLocationTrackerManager.stopTracking();
}
示例代码
以下是完整的示例代码,展示了如何使用background_location_tracker
插件:
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:background_location_tracker/background_location_tracker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';
@pragma('vm:entry-point')
void backgroundCallback() {
BackgroundLocationTrackerManager.handleBackgroundUpdated(
(data) async => Repo().update(data),
);
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await BackgroundLocationTrackerManager.initialize(
backgroundCallback,
config: const BackgroundLocationTrackerConfig(
loggingEnabled: true,
androidConfig: AndroidConfig(
notificationIcon: 'explore',
trackingInterval: Duration(seconds: 4),
distanceFilterMeters: null,
),
iOSConfig: IOSConfig(
activityType: ActivityType.FITNESS,
distanceFilterMeters: null,
restartAfterKill: true,
),
),
);
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var isTracking = false;
Timer? _timer;
List<String> _locations = [];
@override
void initState() {
super.initState();
_getTrackingStatus();
_startLocationsUpdatesStream();
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Container(
width: double.infinity,
child: Column(
children: [
Expanded(
child: Column(
children: [
MaterialButton(
child: const Text('Request location permission'),
onPressed: _requestLocationPermission,
),
if (Platform.isAndroid) ...[
const Text(
'Permission on android is only needed starting from sdk 33.'),
],
MaterialButton(
child: const Text('Request Notification permission'),
onPressed: _requestNotificationPermission,
),
MaterialButton(
child: const Text('Send notification'),
onPressed: () => sendNotification('Hello from another world'),
),
MaterialButton(
child: const Text('Start Tracking'),
onPressed: isTracking
? null
: () async {
await BackgroundLocationTrackerManager.startTracking();
setState(() => isTracking = true);
},
),
MaterialButton(
child: const Text('Stop Tracking'),
onPressed: isTracking
? () async {
await LocationDao().clear();
await _getLocations();
await BackgroundLocationTrackerManager.stopTracking();
setState(() => isTracking = false);
}
: null,
),
],
),
),
const SizedBox(height: 8),
Container(
color: Colors.black12,
height: 2,
),
const Text('Locations'),
MaterialButton(
child: const Text('Refresh locations'),
onPressed: _getLocations,
),
Expanded(
child: Builder(
builder: (context) {
if (_locations.isEmpty) {
return const Text('No locations saved');
}
return ListView.builder(
itemCount: _locations.length,
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
child: Text(
_locations[index],
),
),
);
},
),
),
],
),
),
),
);
}
Future<void> _getTrackingStatus() async {
isTracking = await BackgroundLocationTrackerManager.isTracking();
setState(() {});
}
Future<void> _requestLocationPermission() async {
final result = await Permission.location.request();
if (result == PermissionStatus.granted) {
print('GRANTED'); // ignore: avoid_print
} else {
print('NOT GRANTED'); // ignore: avoid_print
}
}
Future<void> _requestNotificationPermission() async {
final result = await Permission.notification.request();
if (result == PermissionStatus.granted) {
print('GRANTED'); // ignore: avoid_print
} else {
print('NOT GRANTED'); // ignore: avoid_print
}
}
Future<void> _getLocations() async {
final locations = await LocationDao().getLocations();
setState(() {
_locations = locations;
});
}
void _startLocationsUpdatesStream() {
_timer?.cancel();
_timer = Timer.periodic(
const Duration(milliseconds: 250), (timer) => _getLocations());
}
}
class Repo {
static Repo? _instance;
Repo._();
factory Repo() => _instance ??= Repo._();
Future<void> update(BackgroundLocationUpdateData data) async {
final text = 'Location Update: Lat: ${data.lat} Lon: ${data.lon}';
print(text); // ignore: avoid_print
sendNotification(text);
await LocationDao().saveLocation(data);
}
}
class LocationDao {
static const _locationsKey = 'background_updated_locations';
static const _locationSeparator = '-/-/-/';
static LocationDao? _instance;
LocationDao._();
factory LocationDao() => _instance ??= LocationDao._();
SharedPreferences? _prefs;
Future<SharedPreferences> get prefs async =>
_prefs ??= await SharedPreferences.getInstance();
Future<void> saveLocation(BackgroundLocationUpdateData data) async {
final locations = await getLocations();
locations.add(
'${DateTime.now().toIso8601String()} ${data.lat},${data.lon}');
await (await prefs)
.setString(_locationsKey, locations.join(_locationSeparator));
}
Future<List<String>> getLocations() async {
final prefs = await this.prefs;
await prefs.reload();
final locationsString = prefs.getString(_locationsKey);
if (locationsString == null) return [];
return locationsString.split(_locationSeparator);
}
Future<void> clear() async => (await prefs).clear();
}
void sendNotification(String text) {
const settings = InitializationSettings(
android: AndroidInitializationSettings('app_icon'),
iOS: IOSInitializationSettings(
requestAlertPermission: false,
requestBadgePermission: false,
requestSoundPermission: false,
),
);
FlutterLocalNotificationsPlugin().initialize(
settings,
onSelectNotification: (data) async {
print('ON CLICK $data'); // ignore: avoid_print
},
);
FlutterLocalNotificationsPlugin().show(
Random().nextInt(9999),
'Title',
text,
const NotificationDetails(
android: AndroidNotificationDetails('test_notification', 'Test'),
iOS: IOSNotificationDetails(),
),
);
}
通过以上步骤和示例代码,你应该能够成功地在Flutter项目中集成并使用background_location_tracker
插件进行后台位置跟踪。如果有任何问题或需要进一步的帮助,请随时提问!
更多关于Flutter后台定位插件background_location_tracker的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter后台定位插件background_location_tracker的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中集成和使用background_location_tracker
插件的详细步骤,包括必要的代码示例。
1. 添加依赖
首先,在你的pubspec.yaml
文件中添加background_location_tracker
依赖:
dependencies:
flutter:
sdk: flutter
background_location_tracker: ^x.y.z # 请将x.y.z替换为最新版本号
然后运行flutter pub get
来安装依赖。
2. 配置Android权限
打开android/app/src/main/AndroidManifest.xml
,添加以下权限:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.yourapp">
<!-- 其他配置 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<!-- 其他配置 -->
<application
android:label="yourapp"
android:icon="@mipmap/ic_launcher">
<!-- 其他配置 -->
<!-- 声明前台服务 -->
<service
android:name="com.rekab.location.foreground.service.ForegroundService"
android:foregroundServiceType="location" />
<!-- 其他配置 -->
</application>
</manifest>
3. 配置iOS权限
打开ios/Runner/Info.plist
,添加以下权限:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Your app needs access to your location when in use and in the background</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Your app needs access to your location even when it is not in use</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your app needs access to your location when it is in use</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
4. 初始化并使用插件
在你的Flutter项目的Dart代码中,初始化并使用background_location_tracker
插件。以下是一个简单的示例:
import 'package:flutter/material.dart';
import 'package:background_location_tracker/background_location_tracker.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
BackgroundLocationTracker? _locationTracker;
bool _locationServiceEnabled = false;
PermissionStatus? _permissionStatus;
@override
void initState() {
super.initState();
_locationTracker = BackgroundLocationTracker();
// 请求位置权限
_requestLocationPermission();
// 初始化位置服务
_initLocationService();
}
Future<void> _requestLocationPermission() async {
var status = await Permission.locationAlways.request();
setState(() {
_permissionStatus = status;
});
if (status.isGranted) {
// 权限被授予
_initLocationService();
} else if (status.isDenied || status.isPermanentlyDenied) {
// 权限被拒绝
// 可以显示一个对话框提示用户去设置中开启权限
}
}
Future<void> _initLocationService() async {
if (_permissionStatus == PermissionStatus.granted) {
bool serviceEnabled;
LocationAccuracy accuracy = LocationAccuracy.HIGH;
AndroidSettings androidSettings = AndroidSettings(
accuracy: accuracy,
distanceFilter: 10.0,
interval: 60000,
fastestInterval: 12000,
activitiesInterval: 10000,
pauseLocationUpdates: false,
stopOnTerminate: false,
startOnBoot: true,
);
AppleSettings appleSettings = AppleSettings(
accuracy: accuracy,
pauseLocationUpdates: false,
activityType: ActivityType.automotiveNavigation,
allowDeferredLocationUpdatesUntilTraveled: Distance(meters: 1000),
deferredLocationUpdatesInterval: 60,
disableMotionActivityUpdates: false,
);
serviceEnabled = await _locationTracker!.configure(
accuracy: accuracy,
distanceFilter: 10.0,
androidSettings: androidSettings,
appleSettings: appleSettings,
);
if (serviceEnabled) {
setState(() {
_locationServiceEnabled = true;
});
// 开始位置更新
_locationTracker!.startLocationUpdates()
.listen((LocationDto locationDto) {
// 处理位置更新
print('Location: ${locationDto.latitude}, ${locationDto.longitude}');
});
}
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Background Location Tracker'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Location Service Enabled: $_locationServiceEnabled'),
Text('Permission Status: $_permissionStatus'),
],
),
),
),
);
}
}
注意事项
- 权限处理:确保你处理了权限请求的结果,并在必要时提示用户去设置中手动开启权限。
- 后台运行:在iOS上,确保你的应用具有后台运行位置更新的能力。这通常需要在项目的Capabilities中启用后台模式,并添加
UIBackgroundModes
键值对。 - 电池优化:长时间后台运行位置更新可能会影响电池寿命,建议仅在必要时使用。
这个示例展示了如何集成和使用background_location_tracker
插件进行后台位置跟踪。根据你的具体需求,你可能需要调整配置和代码。