Flutter后台定位插件simple_bg_location的使用

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

Flutter后台定位插件simple_bg_location的使用

标题

Flutter Simple Background Location Plugin

内容

The Flutter simple_bg_location plugin provides a basic location API for Android and iOS, specially supporting the recording of location updates in the background on Android devices, even if the user exits the app using the system back button.

Simple_bg_location use FusedLocationProvider or if not available then the LocationManager on Android and CLLocationManager on iOS.

demo

特性

  • Records location updates in the background.
  • Can check background location permission separately.
  • Gets the last known location.
  • Gets the current location.
  • Checks if location services are enabled.
  • Checks if Power Save mode is on or off on Android devices.
  • Custom Notification icon and two custom action.((Android only)
  • Calculates the distance (in meters) between two geo-locations.

开始使用

Android
  1. SDK版本

    • The Simple Background Location Plugin requires the minSdkVersion >= 21 and compileSdkVersion >= 33.
    android {
       compileSdkVersion 33
       ...
    }
    defaultConfig {
       ... 
       minSdkVersion 21
       ...
    }
    
  2. 权限

    • If your App only need approximate accuracy, add ACCESS_COARSE_LOCATION in AndroidManifest.xml file as children of the <manifest> tag.
      <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
      
    • If you need precise accuracy, add both ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION.
      <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
      

    Since Android 10(API level 29), if you need background permission, you must declare the ACCESS_BACKGROUND_LOCATION permission in manifest.

    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    

    More details about location permission.

iOS

Edit info.plist directly(located under ios/Runner)

&lt;dict&gt;
    ...
	&lt;key&gt;NSLocationWhenInUseUsageDescription&lt;/key&gt;
	&lt;string&gt;Why need WhenInUse description&lt;/string&gt;
	&lt;key&gt;NSLocationAlwaysUsageDescription&lt;/key&gt;
	&lt;string&gt;Why need background description&lt;/string&gt;
    ...
&lt;/dict&gt;

使用示例

import 'package:simple_bg_location/simple_bg_location.dart';

void main() async {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Simple BG Location Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<Position> positions = [];
  bool isReady = false;
  double odometer = 0;
  bool isTracking = false;
  final List<LocationEventItem> events = [];

  final scrollController = ScrollController();
  void _scrollToBottom() {
    scrollController.animateTo(scrollController.position.maxScrollExtent,
        duration: const Duration(milliseconds: 300), curve: Curves.easeOut);
  }

  @override
  void initState() {
    super.initState();
    SimpleBgLocation.onPosition(_onPosition, _positionErrorHandle);
    SimpleBgLocation.onNotificationAction(_onNotificationAction);
    SimpleBgLocation.ready().then((sbglState) {
      isReady = true;
      positions.addAll(sbglState.positions ?? []);
      events.addAll(positions.map((e) =&gt; LocationEventItem(
            LocationEventType.position,
            'position record restored from service',
            detail: 'lat: ${e.latitude}, lng: ${e.longitude}',
          )));
      odometer = 0;
      if (positions.length &gt;= 2) {
        for (int i = 1; i &lt; positions.length; i++) {
          final d = SimpleBgLocation.distance(
            positions[i - 1].latitude,
            positions[i - 1].longitude,
            positions[i].latitude,
            positions[i].longitude,
          );
          odometer += d;
        }
      }
      isTracking = sbglState.isTracking;
      setState(() {});
    });
  }

  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance.addPostFrameCallback((_) =&gt; _scrollToBottom());
    return Scaffold(
      appBar: AppBar(
        title: const Text('Simple Background Location Demo'),
      ),
      body: ListView.separated(
        controller: scrollController,
        itemCount: event.length,
        itemBuilder: (context, index) {
          final item = event[index];
          if (item.type == LocationEventType.position) {
            return ListTile(
              dense: true,
              title: Text(
                item.title,
                style: const TextStyle(fontSize: 12.0),
              ),
              subtitle:
                  Text(item.detail, style: const TextStyle(fontSize: 10.0)),
              leading: const Icon(Icons.location_on),
            );
          } else {
            return ListTile(
              dense: true,
              title: Text(item.title, style: const TextStyle(fontSize: 12.0)),
              subtitle:
                  Text(item.detail, style: const TextStyle(fontSize: 10.0)),
              leading: const Icon(Icons.info),
            );
          }
        },
        separatorBuilder: (context, index) {
          return const Divider();
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: isReady
            ? () {
                if (!isTracking) {
                  _startPositionUpdate();
                } else {
                  SimpleBgLocation.stopPositionUpdate();
                  isTracking = false;
                  setState(() {});
                }
              }
            : null,
        tooltip: isTracking ? 'Stop' : 'Start',
        child: Icon(isTracking ? Icons.stop : Icons.play_arrow),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  void _startPositionUpdate() async {
    if (!(await SimpleBgLocation.isLocationServiceEnabled())) {
      event.add(const LocationEventItem(
        LocationEventType.log,
        'Location service disabled',
      ));
      setState(() {});
      SimpleBgLocation.openLocationSettings();
      return;
    }
    var permission = await SimpleBgLocation.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await SimpleBgLocation.requestPermission();
      if (permission == LocationPermission.denied) {
        event.add(const LocationEventItem(
          LocationEventType.log,
          'Permission denied',
        ));
        setState(() {});

        return;
      }
    }

    if (permission == LocationPermission.deniedForever) {
      event.add(const LocationEventItem(
        LocationEventType.log,
        'Permission denied forever',
      ));
      setState(() {});
      // Do not call openAppSetting directly in the formal product.
      // Instead, you should ask the user if they are willing,
      // and do not ask again after the user has refused multiple times.
      SimpleBgLocation.openAppSettings();

      return;
    }

    if ((await SimpleBgDeviceInfo.isPowerSaveMode())) {
      event.add(const LocationEventItem(
        LocationEventType.log,
        'Power save mode enabled!',
        detail: '''Track recording may not work properly in Power Save Mode. 
            If track does not record properly, disable Power Save Mode.''',
      ));
      return;
    }

    final requestSettings = RequestSettings.good();
    requestSettings.notificationConfig = ForegroundNotificationConfig(
      notificationId: 100,
      title: "Simple BG Location",
      text: "distance: {distance}",
      priority: ForegroundNotificationConfig.NOTIFICATION_PRIORITY_DEFAULT,
      actions: ['Action1', 'Action2', 'cancel'],
    );

    final success =
        await SimpleBgLocation.requestPositionUpdate(requestSettings);
    if (success) {
      isTracking = true;
    } else {
      event.add(const LocationEventItem(
        LocationEventType.log,
        'Error',
        detail: 'Request position update failed',
      ));
    }
    setState(() {});
  }

  void _onPosition(Position position) {
    final strEvent = 'lat: ${position.latitude}, lng: ${position.longitude}';
    event.add(LocationEventItem(LocationEventType.position, strEvent));
    setState(() {});
  }

  void _positionErrorHandle(PositionError err) {
    event.add(LocationEventItem(
        LocationEventType.log, 'PositionError CODE: ${err.code}', detail: err.message));
    isTracking = false;
    setState(() {});
  }

  void _onNotificationAction(String action) {
    event.add(LocationEventItem(
      LocationEventType.log,
      'Notification action: $action',
    ));
    setState(() {});
  }
}

class LocationEventItem {
  final String title;
  final String detail;
  final LocationEventType type;

  const LocationEventItem(this.type, this.title, {this.detail = ''});
}

enum LocationEventType { log, position }

示例代码


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

1 回复

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


当然,以下是如何在Flutter项目中使用simple_bg_location插件来实现后台定位的示例代码。simple_bg_location是一个用于在后台持续获取设备位置信息的Flutter插件。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加simple_bg_location依赖:

dependencies:
  flutter:
    sdk: flutter
  simple_bg_location: ^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" />

    <!-- 其他配置 -->

</manifest>

3. 请求权限

在你的Flutter代码中请求位置权限:

import 'package:flutter/material.dart';
import 'package:simple_bg_location/simple_bg_location.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> {
  SimpleBgLocation _simpleBgLocation = SimpleBgLocation();
  bool _isLocationServiceEnabled = false;

  @override
  void initState() {
    super.initState();
    _requestPermissions();
  }

  Future<void> _requestPermissions() async {
    Map<Permission, PermissionStatus> statuses = await [
      Permission.location,
      Permission.activityRecognition,
    ].request();

    if (statuses[Permission.location] == PermissionStatus.granted &&
        statuses[Permission.activityRecognition] == PermissionStatus.granted) {
      setState(() {
        _isLocationServiceEnabled = true;
      });
      _startLocationService();
    }
  }

  void _startLocationService() {
    _simpleBgLocation.startLocationService(
      interval: 5000, // 5秒
      distanceFilter: 10.0, // 10米
      onLocationChanged: (location) {
        print('Location: ${location.latitude}, ${location.longitude}');
      },
      onStatusChanged: (status) {
        print('Location Service Status: $status');
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Background Location Example'),
        ),
        body: Center(
          child: _isLocationServiceEnabled
              ? Text('Location Service is Enabled')
              : Text('Location Permissions are Required'),
        ),
      ),
    );
  }
}

4. 处理后台服务

simple_bg_location插件会自动处理后台服务的创建和管理。不过,你可能需要在Android的Application类中做一些额外的配置来确保后台服务在设备重启后能够自动恢复。

创建一个新的Kotlin/Java类,例如MyApplication.kt(Kotlin)或MyApplication.java(Java),并配置它:

MyApplication.kt (Kotlin)

package com.example.yourapp

import android.app.Application
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
import com.lyokone.location.flutter.background.BackgroundLocationService

class MyApplication: Application() {
    override fun onCreate() {
        super.onCreate()
        FlutterActivity
            .withCachedEngine("my_engine_id")
            .build(this)

        BackgroundLocationService.setPluginRegistrant(object : BackgroundLocationService.PluginRegistrant {
            override fun registerWith(registrar: io.flutter.plugin.common.PluginRegistry.Registrar) {
                GeneratedPluginRegistrant.registerWith(registrar)
            }
        })
    }
}

然后在android/app/src/main/AndroidManifest.xml中指定这个Application类:

<application
    android:name=".MyApplication"
    ... >
    <!-- 其他配置 -->
</application>

5. 停止位置服务

别忘了在适当的时候停止位置服务,例如在应用关闭或用户明确请求停止时:

void _stopLocationService() {
  _simpleBgLocation.stopLocationService();
}

这个示例展示了如何使用simple_bg_location插件在Flutter应用中实现后台定位功能。请根据你的具体需求调整代码。

回到顶部