在flutter中跟踪背景位置时使用background_locator插件时出现问题

67up9zun  于 2023-01-21  发布在  Flutter
关注(0)|答案(3)|浏览(150)

我尝试使用flutter在后台跟踪位置,为此我使用了background_locator插件。它的实现方式是注册了某些静态回调函数。我声明了一个File类型的类变量来在后台保存日志。全局变量在类的最开始构建。
问题:当调用回调方法时,生成的全局变量变为空。因此,尽管我可以在控制台中看到位置日志,但我无法将其写入文件,因为对象为空。
尝试:
1.我已经尝试了他们文档中提供的确切示例。
1.我已经声明它为非静态属性,并试图访问与类对象。
1.尝试将其声明为静态属性。
1.尝试在每次需要时使用相同路径构建文件对象,但它引发以下问题。
在频道www.example.com上找不到方法getApplicationDocumentsDirectory的实现plugins.flutter.io/path_provider
这里是我完整的源代码供参考。

import 'dart:async';
import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';
import 'dart:math';
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 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart' as ph;

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  ReceivePort port = ReceivePort();

  String logStr = '';
  bool isRunning = false;
  LocationDto? lastLocation;

  bool permissionsGranted = false;

  static const String isolateName = 'LocatorIsolate';

  static int _count = -1;

  static File? finalFile;

  void requestPermission() async {
    var storageStatus = await ph.Permission.storage.status;

    if (!storageStatus.isGranted) {
      await ph.Permission.storage.request();
    }

    if (storageStatus.isGranted) {
      permissionsGranted = true;
      setPrerequisites();
    }

    setState(() {});
  }

  static Future<void> init(Map<dynamic, dynamic> params) async {
    //TODO change logs
    print("***********Init callback handler");
    if (params.containsKey('countInit')) {
      dynamic tmpCount = params['countInit'];
      if (tmpCount is double) {
        _count = tmpCount.toInt();
      } else if (tmpCount is String) {
        _count = int.parse(tmpCount);
      } else if (tmpCount is int) {
        _count = tmpCount;
      } else {
        _count = -2;
      }
    } else {
      _count = 0;
    }
    print("$_count");
    await setLogLabel("start");
    final SendPort? send = IsolateNameServer.lookupPortByName(isolateName);
    send?.send(null);
  }

  static Future<void> disposeLocationService() async {
    await setLogLabel("end");
    final SendPort? send = IsolateNameServer.lookupPortByName(isolateName);
    send?.send(null);
  }

  static Future<void> callback(LocationDto locationDto) async {
    await setLogPosition(_count, locationDto);
    final SendPort? send = IsolateNameServer.lookupPortByName(isolateName);
    send?.send(locationDto);
    _count++;
  }

  static Future<void> setLogLabel(String label) async {
    final date = DateTime.now();
    await _MyAppState().writeToLogFile(
        '------------\n$label: ${formatDateLog(date)}\n------------\n');
  }

  static Future<void> setLogPosition(int count, LocationDto data) async {
    final date = DateTime.now();
    await _MyAppState().writeToLogFile(
        '$count : ${formatDateLog(date)} --> ${formatLog(data)} --- isMocked: ${data.isMocked}\n');
  }

  static double dp(double val, int places) {
    num mod = pow(10.0, places);
    return ((val * mod).round().toDouble() / mod);
  }

  static String formatDateLog(DateTime date) {
    return date.hour.toString() +
        ":" +
        date.minute.toString() +
        ":" +
        date.second.toString();
  }

  static String formatLog(LocationDto locationDto) {
    return dp(locationDto.latitude, 4).toString() +
        " " +
        dp(locationDto.longitude, 4).toString();
  }

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

    if (permissionsGranted) {
      setPrerequisites();
    } else {
      requestPermission();
    }
  }

  void setPrerequisites() async {
    finalFile = await _getTempLogFile();

    if (IsolateNameServer.lookupPortByName(isolateName) != null) {
      IsolateNameServer.removePortNameMapping(isolateName);
    }

    IsolateNameServer.registerPortWithName(port.sendPort, isolateName);

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

    setState(() {});
  }

  Future<void> updateUI(LocationDto data) async {
    final log = await 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 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: const 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: () {
          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
    }
  }

  static Future<void> initCallback(Map<dynamic, dynamic> params) async {
    await init(params);
  }

  static Future<void> disposeCallback() async {
    await disposeLocationService();
  }

  Future<void> locationServicecallback(LocationDto locationDto) async {
    await callback(locationDto);
  }

  static Future<void> notificationCallback() async {
    print('***notificationCallback');
  }

  Future<void> writeToLogFile(String log) async {
    await finalFile!.writeAsString(log, mode: FileMode.append);
  }

  Future<String> readLogFile() async {
    return finalFile!.readAsString();
  }

  static Future<File?> _getTempLogFile() async {
    File file =
        File('${(await getApplicationDocumentsDirectory()).path}/log.txt');

    if (file.existsSync()) {
      return file;
    } else {
      file = await file.create(recursive: true);
    }
    return file;
  }

  Future<void> clearLogFile() async {
    await finalFile!.writeAsString('');
  }

  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;
        }
      case PermissionStatus.granted:
        return true;
      default:
        return false;
    }
  }

  Future<void> _startLocator() async {
    Map<String, dynamic> data = {'countInit': 1};
    return await BackgroundLocator.registerLocationUpdate(
      callback,
      initCallback: initCallback,
      initDataCallback: data,
      disposeCallback: disposeCallback,
      iosSettings: const IOSSettings(
          accuracy: LocationAccuracy.NAVIGATION, distanceFilter: 0),
      autoStop: false,
      androidSettings: const 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: notificationCallback,
        ),
      ),
    );
  }
}

任何帮助/建议都将不胜感激。谢谢!

a14dhokn

a14dhokn1#

回调函数没有被调用是我在1.6.12版本中遇到的一个问题。
我通过以下方法解决了问题
1.在github上派生background_locator存储库。
1.将存储库克隆到我的计算机
1.打开location_dto.dart文件并转到fromJson功能。
1.添加了json[密钥.ARG_PROVIDER]??"“而不是
1.已提交并推送到我的分支存储库
1.在pubspec.yaml中,我更新了依赖项,使其指向分支存储库,如下所示:

background_locator:
    git:
      url: git@github.com:frankvollebregt/background_locator.git

如果您发现任何问题,请遵循以下两个github问题:

apeeds0o

apeeds0o2#

background_locator在最新flutter sdk版本上不起作用,在我执行此步骤时起作用

  • FlutterSDK版本应为:3.0.1
  • 在pubspec.yaml文件中更改sdk:“〉=2.8.0〈3.0.0”
  • 不要将代码迁移到null安全
  • 在www.example.com中gradle-wrapper.properties将gradle版本更改为gradle-6.5
  • 安卓/build gradle将扩展Kotlin版本更改为“1.4.31”
  • android/app/build升级版将compileSdkVersion更改为31,将minSdkVersion更改为19,并将targetSdkVersion更改为30
blmhpbnm

blmhpbnm3#

这不是后台定位器插件的问题。当插件/库没有注册到Flutter引擎时,会出现“无实现”错误。
您试图从后台隔离中访问路径提供程序方法。通常,路径提供程序插件将注册到主隔离。
如果要在后台分离菌株中使用它,则必须手动将其注册到引擎。
按照以下步骤将这两个函数添加到location_service_repositary.dart中的Init函数
如果(平台.是Android)路径提供者Android.注册表与();
if(平台.isIOS)路径提供者IOS.寄存器With();
祝你愉快。

相关问题