Flutter后台定位插件background_locator_2的使用

发布于 1周前 作者 songsunli 来自 Flutter

Flutter后台定位插件background_locator_2的使用

background_locator_2 是一个用于在应用被杀死后仍然能够获取位置更新的Flutter插件。本文将介绍如何安装、配置和使用该插件,并提供完整的示例代码。

安装与设置

安装步骤

  1. pubspec.yaml 文件中添加 background_locator_2 依赖:

    dependencies:
      flutter:
        sdk: flutter
      background_locator_2: ^最新版本号
    
  2. 运行 flutter pub get 来安装依赖。

设置步骤

请参考 官方Wiki 获取详细的安装和设置说明。

使用方法

以下是一个完整的示例代码,展示了如何使用 background_locator_2 插件来实现后台定位功能。

import 'dart:async';
import 'dart:isolate';
import 'dart:ui';

import 'package:background_locator_2/background_locator.dart';
import 'package:background_locator_2/location_dto.dart';
import 'package:background_locator_2/settings/android_settings.dart';
import 'package:background_locator_2/settings/ios_settings.dart';
import 'package:background_locator_2/settings/locator_settings.dart';
import 'package:flutter/material.dart';
import 'package:location_permissions/location_permissions.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('com.example.background_location') != null) {
      IsolateNameServer.removePortNameMapping('com.example.background_location');
    }

    IsolateNameServer.registerPortWithName(port.sendPort, 'com.example.background_location');

    port.listen(
      (dynamic data) async {
        await updateUI(data);
      },
    );
    initPlatformState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  Future<void> updateUI(dynamic data) async {
    final log = await FileManager.readLogFile();

    LocationDto locationDto = (data != null) ? LocationDto.fromJson(data) : null;
    await _updateNotificationText(locationDto);

    setState(() {
      if (data != null) {
        lastLocation = locationDto;
      }
      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,
            stopWithTerminate: true),
        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-to-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)));
  }
}

关键点解释

  • 权限管理:确保请求并处理位置权限。
  • 后台服务注册:通过 BackgroundLocator.registerLocationUpdate 方法启动后台定位服务。
  • 通知更新:使用 BackgroundLocator.updateNotificationText 更新通知内容。

结论

通过上述步骤和示例代码,您可以轻松地在Flutter应用中集成后台定位功能。更多详细信息和高级用法,请参考 官方Wiki


更多关于Flutter后台定位插件background_locator_2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter后台定位插件background_locator_2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter应用中使用background_locator_2插件来实现后台定位的示例代码。这个插件允许应用在后台持续获取位置信息。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加background_locator_2及其依赖的依赖项:

dependencies:
  flutter:
    sdk: flutter
  background_locator_2: ^x.y.z  # 请替换为最新版本号
  geolocator: ^x.y.z  # background_locator_2依赖于geolocator

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_BACKGROUND_LOCATION" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    <!-- 其他配置 -->

</manifest>

3. 配置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>

4. 实现后台定位功能

在你的Dart代码中,你需要设置后台定位服务,并处理位置更新。

import 'package:flutter/material.dart';
import 'package:background_locator_2/background_locator_2.dart';
import 'package:geolocator/geolocator.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  configureBackgroundLocator();
  runApp(MyApp());
}

void configureBackgroundLocator() async {
  // 注册位置回调
  BackgroundLocator.registerLocationUpdate(
    locationCallback,
    autoStart: true,
    androidSettings: AndroidSettings(
      accuracy: LocationAccuracy.high,
      distanceFilter: 10.0,
      interval: 60000, // 每分钟更新一次
    ),
    iosSettings: IOSSettings(
      accuracy: LocationAccuracy.best,
      distanceFilter: 10.0,
      activityType: ActivityType.automotiveNavigation,
      pauseLocationUpdatesAutomatically: false,
    ),
  );

  // 请求后台位置权限
  BackgroundLocator.requestPermission().then((status) {
    if (status == LocationPermission.deniedForever) {
      // 用户永久拒绝了位置权限
    } else if (status == LocationPermission.denied) {
      // 用户拒绝了位置权限
    } else if (status == LocationPermission.whileInUse || status == LocationPermission.always) {
      // 用户同意了位置权限
      BackgroundLocator.start();
    }
  });
}

void locationCallback(LocationData location) async {
  // 在这里处理位置更新,比如保存到数据库或发送到服务器
  print('${location.latitude}, ${location.longitude}');
}

class MyApp extends StatelessWidget {
  @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'),
        ),
      ),
    );
  }
}

5. 启动前台服务(可选)

为了保持应用在后台运行时持续获取位置,你可能需要启动一个前台服务。这通常用于Android设备,iOS则不需要。

void startForegroundService() {
  BackgroundLocator.startForegroundService(
    notificationTitle: '位置跟踪',
    notificationMsg: '应用正在后台跟踪您的位置',
    notificationImportance: NotificationImportance.High,
  );
}

你可以在BackgroundLocator.requestPermission().then(...)的回调中调用startForegroundService()函数来启动前台服务。

注意事项

  • 在实际开发中,确保你的应用遵循相关的隐私政策和用户协议,以合法合规的方式使用位置信息。
  • 测试后台定位功能时,请确保设备未连接到调试器,因为某些设备在连接调试器时不会触发后台限制。

这个示例代码展示了如何在Flutter应用中使用background_locator_2插件来实现后台定位功能。根据你的具体需求,你可能需要调整配置和处理逻辑。

回到顶部