dart 如何在后台跟踪用户位置?

s71maibg  于 2023-07-31  发布在  其他
关注(0)|答案(2)|浏览(113)

我有一个Flutter应用程序,它允许管理员通过保存纬度和经度在Map上创建路线。然后管理员可以保存此路线,以便用户可以使用它来查找管理员设置的感兴趣的钓鱼点。然而,我遇到了一个问题,当管理员把他们的手机放进口袋或锁定屏幕时,应用程序停止跟踪路线。有人告诉我,我可以通过使用后台服务来解决这个问题。
以下是我的跟踪代码:

class TrackRouteScreen extends StatefulWidget {
  final String id;

  TrackRouteScreen({required this.id});

  @override
  State<TrackRouteScreen> createState() => _TrackRouteScreenState();
}

class _TrackRouteScreenState extends State<TrackRouteScreen> {
  bool isTracking = false;
  final CollectionReference routesCollection = FirebaseFirestore.instance.collection('Fish     POI');
  late MapController mapController;
  LatLng? currentPosition;
  List<LatLng> polylineCoordinates = [];
  List<Marker> markers = [];
  StreamSubscription<Position>? positionStreamSubscription;

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

  Future<void> initialize() async {
    mapController = MapController();
    currentPosition = await getCurrentLocation();
    setState(() {});
  }

  // Get current location of the user
  Future<LatLng> getCurrentLocation() async {
    bool serviceEnabled;
    LocationPermission permission;

    serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      throw Exception('Location services are disabled');
    }

    permission = await Geolocator.checkPermission();

    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission==LocationPermission.denied||permission == LocationPermission.deniedForever) {
        throw Exception('Location permissions are denied');
      }
    }

    if (permission == LocationPermission.deniedForever) {
      throw Exception('Location permissions are permanently denied');
    }

    Position position = await Geolocator.getCurrentPosition(
      desiredAccuracy: LocationAccuracy.high
    );

    return LatLng(position.latitude, position.longitude);
  }

  // Start tracking
  void startTracking() {
    setState(() {
      isTracking = true;
      polylineCoordinates.clear();
    });

    positionStreamSubscription = Geolocator.getPositionStream(
      locationSettings: LocationSettings()).listen((Position position) {
        updateLocation(position);
      }
    );
  }

  // Update location and polyline
  void updateLocation(Position position) {
    setState(() {    
      currentPosition = LatLng(position.latitude, position.longitude);
      polylineCoordinates.add(currentPosition!);
    });
  }

  // Stop tracking
  void stopTracking() {
    setState(() {
      isTracking = false;
    });
    positionStreamSubscription!.cancel();
    saveTrackedRoute(polylineCoordinates);
  }

  // Save tracked route to Firestore DB
  Future<void> saveTrackedRoute(List<LatLng> trackedRoute) async {
    try {
      final DocumentReference routeDocRef = routesCollection.doc(widget.id);

      await routeDocRef.update({
        'route': trackedRoute .map((latLng) => GeoPoint(latLng.latitude, latLng.longitude))
          .toList(),
      });

      Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => AddImagesScreen(id: widget.id)),
      );
    } catch (error) {
      print('Error saving tracked route: $error');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(body: body());
  }

  Widget body() {
    return Container(
      width: MediaQuery.of(context).size.width,
      height: MediaQuery.of(context).size.height,
      child: Stack(
        children: [
          FlutterMap(
            mapController: mapController,
            options: MapOptions(center: currentPosition, zoom: 15),
            children: [
              TileLayer(
                urlTemplate: mapboxUrlTemplate,
                additionalOptions: {
                  'accessToken': mapboxAccessToken,
                  'id': 'mapbox.mapbox-streets-v8',
                },
              ),
              PolylineLayer(
                polylines: [
                  Polyline(
                    points: polylineCoordinates,
                    strokeWidth: 5,
                    color: Colors.blue,
                  ),
                ],
              ),
              CurrentLocationLayer(
                followOnLocationUpdate: FollowOnLocationUpdate.always,
                style: LocationMarkerStyle(
                  marker: DefaultLocationMarker(),
                ),
              ),
            ],
          ),
          Padding(
            padding: EdgeInsets.symmetric(horizontal: 15, vertical: 20),
            child: Align(
              alignment: Alignment.bottomCenter,
              child: GradientElevatedButton(
                onTap: isTracking ? stopTracking : startTracking,
                width: MediaQuery.of(context).size.width,
                beginningColor: isTracking ? Colors.red : Colors.green,
                endingColor: isTracking ? Colors.red : Colors.green,
                text: isTracking ? 'Stop' : 'Start',
              ),
            ),
          ),
          Positioned(
            top: 40.0,
            right: 15.0,
            child: FloatingActionButton(
              onPressed: () {
                if (isTracking) {
                  showDialog(
                    context: context,
                    builder: (BuildContext context) {
                      return AlertDialog(
                        title: Text('Quit Tracking'),
                        content: Text('Are you sure you want to quit tracking?'),
                        actions: <Widget>[
                          TextButton(
                            child: Text('Cancel'),
                            onPressed: () {
                              Navigator.of(context).pop();
                            },
                          ),
                          TextButton(
                            child: Text('Quit'),
                            onPressed: () {
                              Navigator.of(context).pop();
                              Navigator.of(context).pop();
                              stopTracking();
                            },
                          ),
                        ],
                      );
                    },
                  );
                } else {
                  Navigator.pop(context);
                }
              },
              child: Icon(Icons.close),
              backgroundColor: Colors.grey[300],
              foregroundColor: Colors.black,
            ),
          ),
        ],
      ),
    );
  }
}

字符串
作为一个17岁的女孩谁是新的应用程序开发,我觉得这个概念令人困惑。在后台跟踪是什么意思?我该如何实施?此外,我担心可能没有数据连接的用户。在这种情况下,数据可以存储在哪里,直到连接再次可用?
有人能给予我一个坚实的理解这一点,修改代码,并解释它在一个简单的方式?哪个套餐适合这个?

htrmnn0y

htrmnn0y1#

如果我正确理解你的目的,你可以尝试这个软件包https://pub.dev/packages/flutter_background_service

tkclm6bt

tkclm6bt2#

令人印象深刻的是,你在这么年轻的时候就在追求应用程序开发!我将尽我所能解释背景跟踪并帮助您简化代码。不是说我是这方面的专业人士,但似乎人们忽视了你。
后台跟踪是指即使应用未处于活动状态或屏幕已锁定,应用仍能够继续跟踪用户的位置。这对于管理员希望在不保持应用程序始终打开的情况下跟踪路线的情况至关重要,例如使用钓鱼应用程序。
要实现后台监控,请使用background_location_tracker Flutter包。此包使您能够在后台监视用户的位置,并在接收到位置更新时执行代码。
使用background_location_tracker在后台实现位置监控:

dependencies:
  flutter:
    sdk: flutter
  background_location_tracker: ^1.4.1  // Or the newest version

字符串
此外,compileSdkVersion和targetSdkVersion应该至少为29。修改android/app/build.gradle如下:

android {
  ...
  compileSdkVersion 29
  ...

  defaultConfig {
    ...
    targetSdkVersion 29
    ...
  }
  ...
}

  • 在终端中运行flutter pub get来安装软件包。

在你的主.dart文件中,你应该初始化这个包。这就是你的主.dart文件的样子:

@pragma('vm:entry-point')
void backgroundCallback() {
  BackgroundLocationTrackerManager.handleBackgroundUpdated(
    (data) async {},
  );
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  await BackgroundLocationTrackerManager.initialize(
    backgroundCallback,
    config: BackgroundLocationTrackerConfig(
      loggingEnabled: true,
      androidConfig: AndroidConfig(
        trackingInterval: Duration(seconds: 3),
      ),
      iOSConfig: IOSConfig(
        activityType: ActivityType.NAVIGATION,
        restartAfterKill: true,
      ),
    ),
  );
  runApp(RodRoute());
}

class RodRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SplashScreen(),
    );
  }
}


初始化包后,让我们修改TrackRouteScreen。首先,导入必要的包:

import 'dart:async';
import 'package:background_location_tracker/background_location_tracker.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart';
import 'package:flutter/material.dart';


使用后台位置跟踪所需的修改更新_TrackRouteScreenState类。添加必要的成员变量:

bool isTracking = false;
final CollectionReference routesCollection = FirebaseFirestore.instance.collection('Fishing POI');
late MapController mapController;
LatLng? currentPosition;
List<LatLng> polylineCoordinates = [];
List<Marker> markers = [];
StreamSubscription<Position>? positionStreamSubscription;


添加您需要的功能:

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

  Future<void> initialize() async {
    mapController = MapController();
    currentPosition = await getCurrentLocation();

    setState(() {});
  }

  // Get current location of the user
  Future<LatLng> getCurrentLocation() async {
    bool serviceEnabled;
    LocationPermission permission;

    serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      throw Exception('Location services disabled');
    }

    permission = await Geolocator.checkPermission();

    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) {
        throw Exception('Location permissions denied');
      }
    }

    Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high, forceAndroidLocationManager: true);
    return LatLng(position.latitude, position.longitude);
  }

  // Start tracking
  void startTracking() async {
    setState(() {
      isTracking = true;
      polylineCoordinates.clear();
    });

    await BackgroundLocationTrackerManager.startTracking(
      config: AndroidConfig(trackingInterval: Duration(seconds: 3)),
    );

    positionStreamSubscription = Geolocator.getPositionStream(locationSettings: LocationSettings()).listen((Position position) {
      updateLocation(position);
    });
  }

  // Update location and polyline
  void updateLocation(Position position) async {
    setState(() {
      currentPosition = LatLng(position.latitude, position.longitude);
      polylineCoordinates.add(currentPosition!);
    });
  }

  // Stop tracking
  void stopTracking() {
    setState(() {
      isTracking = false;
    });

    positionStreamSubscription!.cancel();
    saveTrackedRoute(polylineCoordinates);
  }

  // Save the tracked route to firestore db
  Future<void> saveTrackedRoute(List<LatLng> trackedRoute) async {
    try {
      final DocumentReference routeDocRef = routesCollection.doc(widget.id);
      List<GeoPoint> geoPoints = trackedRoute.map((latLng) => GeoPoint(latLng.latitude, latLng.longitude)).toList();

      await routeDocRef.update({
        'trackedRoute': FieldValue.arrayUnion(geoPoints),
      });
    } catch (error) {
      print(error);
    }
  }


确保在按下“开始”按钮时调用startTracking函数,并确保在按下“停止”按钮时调用stopTracking函数。
现在,即使屏幕被锁定或应用处于后台,您的应用也会监控用户的位置。
关于您对没有数据连接的用户的担忧,您可以使用数据库或文件在本地存储跟踪数据,直到数据连接可用。使用sqflite这样的包将数据存储在本地SQLite数据库中,或者使用shared_preferences将数据存储为设备上的键值对。当存在可用的数据连接时,您可以将数据提交到Firestore数据库。但这是它自己的主题,你可以在另一个问题中问。
我希望这些解释和代码修改能让你更容易理解和在应用中实现后台跟踪。

相关问题