欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

本文基于flutter3.27.5开发

一、location 库概述

位置服务是移动应用的核心功能之一,广泛应用于地图导航、位置打卡、运动追踪、本地服务推荐等场景。在 Flutter for OpenHarmony 应用开发中,location 是一个功能完善的位置服务插件,提供了完整的跨平台定位能力。

location 库特点

location 库基于 Flutter 平台接口实现,提供了以下核心特性:

精确定位:支持高精度定位,提供经纬度、海拔、速度、方向等完整位置信息。

实时追踪:支持位置变化监听,实时获取设备移动轨迹。

权限管理:内置权限检查和请求机制,简化权限处理流程。

后台定位:支持后台模式,应用在后台时也能持续获取位置信息。

灵活配置:支持精度、更新间隔、距离阈值等多种参数配置。

OpenHarmony 优化:提供场景模式等特有功能,针对不同使用场景优化定位策略。

功能支持对比

功能 Android iOS OpenHarmony
单次定位
持续追踪
后台定位
权限管理
精度控制
场景模式
距离过滤

使用场景:地图导航、运动追踪、外卖配送、打车应用、位置打卡、本地服务推荐等。


二、安装与配置

2.1 添加依赖

在项目的 pubspec.yaml 文件中添加 location 依赖:

dependencies:
  location:
    git:
      url: https://atomgit.com/openharmony-sig/flutter_location.git
      path: packages/location

然后执行以下命令获取依赖:

flutter pub get

2.2 权限配置

OpenHarmony 需要在配置文件中声明位置权限。在 ohos/entry/src/main/module.json5 中添加:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION",
        "reason": "$string:location_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.LOCATION",
        "reason": "$string:location_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "inuse"
        }
      }
    ]
  }
}

ohos/entry/src/main/resources/base/element/string.json 中添加权限说明:

{
  "string": [
    {
      "name": "location_reason",
      "value": "应用需要位置权限来提供定位服务"
    }
  ]
}

三、核心 API 详解

3.1 Location 类

Location 是位置服务的核心类,提供单例模式访问。

class Location {
  static Location get instance;
  
  Future<LocationData> getLocation();
  Stream<LocationData> get onLocationChanged;
  Future<PermissionStatus> hasPermission();
  Future<PermissionStatus> requestPermission();
  Future<bool> serviceEnabled();
  Future<bool> requestService();
  Future<bool> changeSettings({
    LocationAccuracy? accuracy,
    int? interval,
    double? distanceFilter,
  });
  Future<bool> enableBackgroundMode({bool enable = true});
  Future<bool> isBackgroundModeEnabled();
}

3.2 getLocation 方法

getLocation 方法用于获取当前位置信息。

Future<LocationData> getLocation()

返回类型LocationData - 包含位置信息的对象

使用示例

final location = Location.instance;
final locationData = await location.getLocation();
print('纬度: ${locationData.latitude}');
print('经度: ${locationData.longitude}');

3.3 onLocationChanged 属性

onLocationChanged 属性提供位置变化的监听流。

Stream<LocationData> get onLocationChanged

使用示例

final subscription = location.onLocationChanged.listen((LocationData locationData) {
  print('位置更新: ${locationData.latitude}, ${locationData.longitude}');
});

// 停止监听
subscription.cancel();

3.4 hasPermission 方法

hasPermission 方法用于检查位置权限状态。

Future<PermissionStatus> hasPermission()

返回类型PermissionStatus - 权限状态枚举

权限状态

  • PermissionStatus.denied:权限被拒绝
  • PermissionStatus.deniedForever:权限被永久拒绝
  • PermissionStatus.granted:权限已授予
  • PermissionStatus.grantedLimited:授予有限权限
  • PermissionStatus.restricted:权限受限

使用示例

final status = await location.hasPermission();
if (status == PermissionStatus.denied) {
  // 请求权限
  await location.requestPermission();
}

3.5 requestPermission 方法

requestPermission 方法用于请求位置权限。

Future<PermissionStatus> requestPermission()

使用示例

final status = await location.requestPermission();
if (status == PermissionStatus.granted) {
  // 权限已授予,可以开始定位
  final locationData = await location.getLocation();
}

3.6 serviceEnabled 方法

serviceEnabled 方法用于检查位置服务是否启用。

Future<bool> serviceEnabled()

使用示例

final enabled = await location.serviceEnabled();
if (!enabled) {
  // 请求启用位置服务
  await location.requestService();
}

3.7 requestService 方法

requestService 方法用于请求启用位置服务。

Future<bool> requestService()

使用示例

final enabled = await location.serviceEnabled();
if (!enabled) {
  final result = await location.requestService();
  if (result) {
    print('位置服务已启用');
  }
}

3.8 changeSettings 方法

changeSettings 方法用于修改定位设置。

Future<bool> changeSettings({
  LocationAccuracy? accuracy,
  int? interval,
  double? distanceFilter,
})

参数说明

  • accuracy:定位精度(LocationAccuracy 枚举)
  • interval:更新间隔(毫秒)
  • distanceFilter:距离过滤(米)

精度等级

  • LocationAccuracy.lowest:最低精度
  • LocationAccuracy.low:低精度
  • LocationAccuracy.medium:中等精度
  • LocationAccuracy.high:高精度
  • LocationAccuracy.best:最佳精度
  • LocationAccuracy.bestForNavigation:导航最佳精度

使用示例

await location.changeSettings(
  accuracy: LocationAccuracy.high,
  interval: 1000,
  distanceFilter: 10,
);

3.9 LocationData 类

LocationData 表示位置信息数据模型。

class LocationData {
  final double? latitude;      // 纬度
  final double? longitude;     // 经度
  final double? accuracy;      // 精度(米)
  final double? altitude;      // 海拔(米)
  final double? speed;         // 速度(米/秒)
  final double? speedAccuracy; // 速度精度
  final double? heading;       // 方向(度)
  final double? headingAccuracy; // 方向精度
  final int? timestamp;        // 时间戳
}

3.10 PermissionStatus 枚举

PermissionStatus 表示权限状态。

enum PermissionStatus {
  denied,           // 权限被拒绝
  deniedForever,    // 权限被永久拒绝
  granted,          // 权限已授予
  grantedLimited,   // 授予有限权限
  restricted,       // 权限受限
}

3.11 LocationAccuracy 枚举

LocationAccuracy 表示定位精度等级。

enum LocationAccuracy {
  lowest,              // 最低精度
  low,                 // 低精度
  medium,              // 中等精度
  high,                // 高精度
  best,                // 最佳精度
  bestForNavigation,   // 导航最佳精度
}

四、OpenHarmony 特有功能

4.1 LocationScenario 枚举

OpenHarmony 提供了场景模式,针对不同使用场景优化定位策略。

enum LocationScenario {
  unset,              // 未设置
  navigation,         // 导航场景
  trajectoryTracking, // 轨迹追踪场景
  carHailing,         // 网约车场景
  dailyLifeService,   // 生活服务场景
  noPower,            // 无功耗场景
}

4.2 changeSettings 扩展参数

OpenHarmony 平台的 changeSettings 方法支持额外参数:

Future<bool> changeSettings({
  LocationAccuracy? accuracy,
  int? interval,
  double? distanceFilter,
  LocationScenario? scenario,  // OpenHarmony 特有
})

使用示例

await location.changeSettings(
  accuracy: LocationAccuracy.high,
  scenario: LocationScenario.navigation,
  interval: 1000,
);

五、OpenHarmony 平台实现原理

5.1 原生 API 映射

location 在 OpenHarmony 平台上使用 @kit.LocationKit 模块实现:

Flutter API OpenHarmony API
getLocation geoLocationManager.getCurrentLocation
onLocationChanged geoLocationManager.onLocationChange
hasPermission abilityAccessCtrl.verifyAccessToken
requestPermission requestPermissionsFromUser
serviceEnabled geoLocationManager.isLocationEnabled
requestService openLink
changeSettings geoLocationManager.updateLocationRequest

5.2 单次定位实现

OpenHarmony 使用 geoLocationManager.getCurrentLocation 获取当前位置:

async getCurrentLocation(): Promise<LocationData> {
  let request: geoLocationManager.SingleLocationRequest = {
    token: this.token,
    timeout: 30,
  };
  
  let location = await geoLocationManager.getCurrentLocation(request);
  
  return {
    latitude: location.latitude,
    longitude: location.longitude,
    altitude: location.altitude,
    accuracy: location.accuracy,
    speed: location.speed,
    heading: location.direction,
    timestamp: Date.now(),
  };
}

5.3 持续定位实现

OpenHarmony 使用 geoLocationManager.onLocationChange 监听位置变化:

startLocationUpdates(callback: (location: LocationData) => void) {
  let request: geoLocationManager.LocationRequest = {
    priority: geoLocationManager.LocationRequestPriority.FIRST_FIX,
    scenario: geoLocationManager.LocationRequestScenario.UNSET,
    timeInterval: 1,
    distanceInterval: 0,
    maxAccuracy: 100,
  };
  
  geoLocationManager.onLocationChange(request, (location) => {
    callback({
      latitude: location.latitude,
      longitude: location.longitude,
      altitude: location.altitude,
      accuracy: location.accuracy,
      speed: location.speed,
      heading: location.direction,
      timestamp: Date.now(),
    });
  });
}

5.4 权限检查实现

OpenHarmony 使用 abilityAccessCtrl 检查权限状态:

async hasPermission(): Promise<boolean> {
  let atManager = abilityAccessCtrl.createAtManager();
  let grantStatus = await atManager.verifyAccessToken(
    this.context.applicationInfo.accessTokenId,
    'ohos.permission.LOCATION'
  );
  
  return grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
}

5.5 场景模式实现

OpenHarmony 支持多种定位场景模式:

updateLocationRequest(scenario: LocationScenario) {
  let request: geoLocationManager.LocationRequest = {
    priority: geoLocationManager.LocationRequestPriority.FIRST_FIX,
    scenario: this.mapScenario(scenario),
    timeInterval: 1,
    distanceInterval: 0,
    maxAccuracy: 100,
  };
  
  geoLocationManager.updateLocationRequest(request);
}

mapScenario(scenario: LocationScenario): geoLocationManager.LocationRequestScenario {
  switch (scenario) {
    case LocationScenario.navigation:
      return geoLocationManager.LocationRequestScenario.NAVIGATION;
    case LocationScenario.trajectoryTracking:
      return geoLocationManager.LocationRequestScenario.TRAJECTORY_TRACKING;
    case LocationScenario.carHailing:
      return geoLocationManager.LocationRequestScenario.CAR_HAILING;
    case LocationScenario.dailyLifeService:
      return geoLocationManager.LocationRequestScenario.DAILY_LIFE_SERVICE;
    case LocationScenario.noPower:
      return geoLocationManager.LocationRequestScenario.NO_POWER;
    default:
      return geoLocationManager.LocationRequestScenario.UNSET;
  }
}

六、MethodChannel 通信协议

6.1 方法列表

方法 参数 返回值 说明
getLocation - LocationData 获取当前位置
hasPermission - PermissionStatus 检查权限状态
requestPermission - PermissionStatus 请求权限
serviceEnabled - bool 检查服务状态
requestService - bool 请求启用服务
changeSettings accuracy, interval, distanceFilter, scenario bool 修改设置

6.2 LocationData 数据格式

{
  "latitude": 39.9042,
  "longitude": 116.4074,
  "altitude": 50.0,
  "accuracy": 10.0,
  "speed": 0.0,
  "speedAccuracy": 0.0,
  "heading": 0.0,
  "headingAccuracy": 0.0,
  "timestamp": 1234567890000
}

6.3 PermissionStatus 数据格式

{
  "status": "granted"  // denied, deniedForever, granted, grantedLimited, restricted
}

七、实战案例

7.1 基础定位功能

// 检查位置服务状态和权限
bool serviceEnabled = await location.serviceEnabled();
if (!serviceEnabled) {
  serviceEnabled = await location.requestService();
}

PermissionStatus permissionStatus = await location.hasPermission();
if (permissionStatus == PermissionStatus.denied) {
  permissionStatus = await location.requestPermission();
}

// 获取当前位置
LocationData locationData = await location.getLocation();
print('纬度: ${locationData.latitude}');
print('经度: ${locationData.longitude}');
print('精度: ${locationData.accuracy}米');

7.2 实时位置追踪

// 开始实时位置监听
StreamSubscription<LocationData> subscription = 
    location.onLocationChanged.listen((LocationData locationData) {
  print('位置更新 - 纬度: ${locationData.latitude}, 经度: ${locationData.longitude}');
});

// 停止监听
subscription.cancel();

7.3 场景模式配置

// 配置导航场景
await location.changeSettings(
  accuracy: LocationAccuracy.high,
  scenario: LocationScenario.navigation,
  interval: 1000,
);

// 配置运动追踪场景
await location.changeSettings(
  accuracy: LocationAccuracy.balanced,
  scenario: LocationScenario.trajectoryTracking,
  interval: 5000,
);

7.4 权限管理

// 检查权限状态
PermissionStatus status = await location.hasPermission();
if (status == PermissionStatus.denied) {
  status = await location.requestPermission();
}

// 权限状态处理
switch (status) {
  case PermissionStatus.granted:
    // 权限已授予
    break;
  case PermissionStatus.denied:
    // 权限被拒绝
    break;
  case PermissionStatus.deniedForever:
    // 权限被永久拒绝
    break;
}

八、最佳实践

8.1 权限处理最佳实践

Future<bool> checkAndRequestLocationPermission() async {
  final location = Location.instance;
  
  // 检查服务状态
  bool serviceEnabled = await location.serviceEnabled();
  if (!serviceEnabled) {
    serviceEnabled = await location.requestService();
    if (!serviceEnabled) {
      return false;
    }
  }
  
  // 检查权限状态
  PermissionStatus permissionStatus = await location.hasPermission();
  if (permissionStatus == PermissionStatus.denied) {
    permissionStatus = await location.requestPermission();
  }
  
  return permissionStatus == PermissionStatus.granted;
}

8.2 错误处理

Future<LocationData?> getLocationSafely() async {
  try {
    final location = Location.instance;
    return await location.getLocation();
  } catch (e) {
    print('定位失败: $e');
    return null;
  }
}

8.3 性能优化

// 根据应用场景选择合适的精度
LocationAccuracy getAccuracyForScenario(String scenario) {
  switch (scenario) {
    case 'navigation':
      return LocationAccuracy.bestForNavigation;
    case 'fitness':
      return LocationAccuracy.high;
    case 'general':
      return LocationAccuracy.medium;
    default:
      return LocationAccuracy.low;
  }
}

// 根据应用场景选择合适的场景模式
LocationScenario getScenarioForApp(String appType) {
  switch (appType) {
    case 'navigation':
      return LocationScenario.navigation;
    case 'fitness':
      return LocationScenario.trajectoryTracking;
    case 'rideHailing':
      return LocationScenario.carHailing;
    case 'localService':
      return LocationScenario.dailyLifeService;
    default:
      return LocationScenario.unset;
  }
}

8.4 位置数据缓存

class LocationCache {
  static LocationData? _lastLocation;
  static DateTime? _lastUpdateTime;
  
  static Future<LocationData?> getCachedLocation() async {
    if (_lastLocation != null && _lastUpdateTime != null) {
      final difference = DateTime.now().difference(_lastUpdateTime!);
      if (difference.inSeconds < 30) {
        return _lastLocation;
      }
    }
    
    final location = Location.instance;
    final locationData = await location.getLocation();
    _lastLocation = locationData;
    _lastUpdateTime = DateTime.now();
    return locationData;
  }
}

8.5 后台定位配置

Future<void> setupBackgroundLocation() async {
  final location = Location.instance;
  
  // 启用后台定位模式
  bool backgroundMode = await location.isBackgroundModeEnabled();
  if (!backgroundMode) {
    await location.enableBackgroundMode(enable: true);
  }
  
  // 配置定位参数
  await location.changeSettings(
    accuracy: LocationAccuracy.high,
    interval: 5000,
    distanceFilter: 10,
  );
}

九、常见问题

Q1:定位失败怎么办?

检查以下几点:

  1. 确保权限已授予

    PermissionStatus status = await location.hasPermission();
    if (status != PermissionStatus.granted) {
      await location.requestPermission();
    }
    
  2. 确保位置服务已启用

    bool enabled = await location.serviceEnabled();
    if (!enabled) {
      await location.requestService();
    }
    
  3. 检查设备是否支持定位功能

Q2:定位精度不准确怎么办?

提高定位精度:

await location.changeSettings(
  accuracy: LocationAccuracy.best,
  interval: 1000,
);

Q3:如何实现后台定位?

  1. 配置后台定位权限
  2. 启用后台定位模式
await location.enableBackgroundMode(enable: true);

Q4:如何节省电量?

使用低精度定位和较长的更新间隔:

await location.changeSettings(
  accuracy: LocationAccuracy.low,
  interval: 10000,  // 10秒更新一次
  scenario: LocationScenario.noPower,
);

Q5:如何处理权限被永久拒绝?

引导用户到设置页面:

PermissionStatus status = await location.hasPermission();
if (status == PermissionStatus.deniedForever) {
  // 提示用户到设置页面手动授权
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: Text('需要位置权限'),
      content: Text('请在设置中授予应用位置权限'),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: Text('取消'),
        ),
        TextButton(
          onPressed: () {
            // 打开应用设置页面
            openAppSettings();
          },
          child: Text('去设置'),
        ),
      ],
    ),
  );
}

Q6:OpenHarmony 场景模式如何选择?

根据应用类型选择合适的场景模式:

  • 导航应用LocationScenario.navigation
  • 运动追踪LocationScenario.trajectoryTracking
  • 网约车LocationScenario.carHailing
  • 本地服务LocationScenario.dailyLifeService
  • 低功耗LocationScenario.noPower

Q7:为什么海拔、速度、方向等字段显示为未知?

问题描述:获取到的 LocationData 中,altitude(海拔)、speed(速度)、heading(方向)、time(时间)等字段返回 null,显示为"未知"。

原因分析:这是因为 OpenHarmony 原生代码目前只返回了 3 个基础字段:

// OpenHarmony 原生代码 (FlutterLocation.ets)
loc.set("latitude", location.latitude);
loc.set("longitude", location.longitude);
loc.set("accuracy", location.accuracy);

LocationData 类期望的完整字段包括:

  • altitude(海拔)
  • speed(速度)
  • heading(方向)
  • time(时间戳)
  • speedAccuracy(速度精度)
  • verticalAccuracy(垂直精度)
  • provider(位置提供者)
  • 等等…

影响范围

  • 经纬度 (latitude, longitude) 和精度 (accuracy) 可以正常获取
  • 海拔、速度、方向等扩展字段为 null

解决方案:如果需要完整的定位信息,可以修改原生代码添加更多字段映射,或者在应用层对 null 值做兼容处理。

应用层兼容示例

LocationData? location = await location.getLocation();

double latitude = location?.latitude ?? 0.0;
double altitude = location?.altitude ?? 0.0;  // 可能为null
double speed = location?.speed ?? 0.0;        // 可能为null
double heading = location?.heading ?? 0.0;    // 可能为null

// 安全访问
if (location?.altitude != null) {
  print('海拔: ${location!.altitude}');
} else {
  print('海拔: 未知(设备不支持或权限不足)');
}

十、总结

location 库为 Flutter for OpenHarmony 开发提供了完整的位置服务能力。通过丰富的 API,开发者可以实现单次定位、持续追踪、后台定位等功能。该库在鸿蒙平台上已经完成了完整的适配,支持所有核心功能,包括 OpenHarmony 特有的场景模式,开发者可以放心使用。

核心特性

  1. 完整功能:支持单次定位、持续追踪、后台定位等完整的位置服务功能
  2. 跨平台一致:API 设计与 Android、iOS 平台保持一致,降低学习成本
  3. OpenHarmony 优化:提供场景模式等特有功能,针对不同使用场景优化定位策略
  4. 易于集成:提供清晰的权限管理机制和丰富的示例代码

使用建议

  1. 在使用前先检查权限和服务状态
  2. 根据应用场景选择合适的精度和场景模式
  3. 合理设置更新间隔,平衡精度和功耗
  4. 及时释放资源,避免内存泄漏

十一、完整代码示例

以下是一个完整的可运行示例,展示了 location 库的核心功能:
在这里插入图片描述

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:location/location.dart';

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

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Location Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const HomePage(),
    );
  }
}

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

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final Location location = Location.instance;
  LocationData? _currentLocation;
  PermissionStatus? _permissionStatus;
  bool _serviceEnabled = false;
  bool _isTracking = false;
  StreamSubscription<LocationData>? _locationSubscription;
  List<LocationData> _locationHistory = [];
  String _statusMessage = '';

  
  void initState() {
    super.initState();
    _checkLocationService();
  }

  
  void dispose() {
    _locationSubscription?.cancel();
    super.dispose();
  }

  Future<void> _checkLocationService() async {
    _serviceEnabled = await location.serviceEnabled();
    if (!_serviceEnabled) {
      _serviceEnabled = await location.requestService();
    }
    
    _permissionStatus = await location.hasPermission();
    if (_permissionStatus == PermissionStatus.denied) {
      _permissionStatus = await location.requestPermission();
    }
    
    setState(() {});
  }

  void _showMessage(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message)),
    );
  }

  Future<void> _getCurrentLocation() async {
    try {
      final locationData = await location.getLocation();
      setState(() {
        _currentLocation = locationData;
        _statusMessage = '获取位置成功';
      });
      _showMessage('位置已更新');
    } catch (e) {
      setState(() {
        _statusMessage = '获取位置失败: $e';
      });
      _showMessage('获取位置失败');
    }
  }

  Future<void> _startTracking() async {
    await _checkLocationService();
    
    await location.changeSettings(
      accuracy: LocationAccuracy.high,
      interval: 1000,
      distanceFilter: 10,
    );
    
    _locationSubscription = location.onLocationChanged.listen((LocationData locationData) {
      setState(() {
        _currentLocation = locationData;
        _locationHistory.add(locationData);
        if (_locationHistory.length > 100) {
          _locationHistory.removeAt(0);
        }
      });
    });
    
    setState(() {
      _isTracking = true;
      _statusMessage = '开始追踪位置';
    });
    _showMessage('开始追踪位置');
  }

  void _stopTracking() {
    _locationSubscription?.cancel();
    setState(() {
      _isTracking = false;
      _statusMessage = '停止追踪位置';
    });
    _showMessage('停止追踪位置');
  }

  void _clearHistory() {
    setState(() {
      _locationHistory.clear();
      _statusMessage = '历史记录已清空';
    });
    _showMessage('历史记录已清空');
  }

  Future<void> _changeScenario(String scenario) async {
    LocationScenario locationScenario;
    switch (scenario) {
      case 'navigation':
        locationScenario = LocationScenario.navigation;
        break;
      case 'tracking':
        locationScenario = LocationScenario.trajectoryTracking;
        break;
      case 'carHailing':
        locationScenario = LocationScenario.carHailing;
        break;
      case 'dailyLife':
        locationScenario = LocationScenario.dailyLifeService;
        break;
      default:
        locationScenario = LocationScenario.unset;
    }
    
    await location.changeSettings(
      accuracy: LocationAccuracy.high,
      scenario: locationScenario,
      interval: 1000,
    );
    
    setState(() {
      _statusMessage = '场景模式已切换: $scenario';
    });
    _showMessage('场景模式已切换');
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('位置服务示例'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        actions: [
          PopupMenuButton<String>(
            onSelected: _changeScenario,
            itemBuilder: (context) => [
              const PopupMenuItem(
                value: 'navigation',
                child: Text('导航场景'),
              ),
              const PopupMenuItem(
                value: 'tracking',
                child: Text('轨迹追踪'),
              ),
              const PopupMenuItem(
                value: 'carHailing',
                child: Text('网约车场景'),
              ),
              const PopupMenuItem(
                value: 'dailyLife',
                child: Text('生活服务'),
              ),
            ],
          ),
        ],
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            _buildInfoCard('服务状态', _serviceEnabled ? '已启用' : '未启用'),
            const SizedBox(height: 16),
            _buildInfoCard('权限状态', _permissionStatus?.toString() ?? '未知'),
            const SizedBox(height: 16),
            if (_statusMessage.isNotEmpty) ...[
              Card(
                color: Colors.blue.shade50,
                child: Padding(
                  padding: const EdgeInsets.all(16),
                  child: Text(_statusMessage),
                ),
              ),
              const SizedBox(height: 16),
            ],
            if (_currentLocation != null) ...[
              _buildLocationInfo(),
              const SizedBox(height: 16),
            ],
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton.icon(
                  onPressed: _getCurrentLocation,
                  icon: const Icon(Icons.my_location),
                  label: const Text('获取位置'),
                ),
                ElevatedButton.icon(
                  onPressed: _isTracking ? _stopTracking : _startTracking,
                  icon: Icon(_isTracking ? Icons.stop : Icons.play_arrow),
                  label: Text(_isTracking ? '停止追踪' : '开始追踪'),
                ),
              ],
            ),
            const SizedBox(height: 16),
            ElevatedButton.icon(
              onPressed: _clearHistory,
              icon: const Icon(Icons.clear),
              label: const Text('清空历史记录'),
            ),
            if (_locationHistory.isNotEmpty) ...[
              const SizedBox(height: 16),
              Text('位置历史 (${_locationHistory.length} 个点)', 
                style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
              const SizedBox(height: 8),
              SizedBox(
                height: 200,
                child: ListView.builder(
                  itemCount: _locationHistory.length,
                  itemBuilder: (context, index) {
                    final locationData = _locationHistory[index];
                    return ListTile(
                      leading: CircleAvatar(child: Text('${index + 1}')),
                      title: Text('纬度: ${locationData.latitude?.toStringAsFixed(6)}, 经度: ${locationData.longitude?.toStringAsFixed(6)}'),
                      subtitle: Text('精度: ${locationData.accuracy?.toStringAsFixed(1)}米'),
                    );
                  },
                ),
              ),
            ],
          ],
        ),
      ),
    );
  }

  Widget _buildInfoCard(String title, String value) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
            Text(value),
          ],
        ),
      ),
    );
  }

  Widget _buildLocationInfo() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text('当前位置信息', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
            const Divider(),
            _buildLocationRow('纬度', _currentLocation!.latitude?.toStringAsFixed(6) ?? '未知'),
            _buildLocationRow('经度', _currentLocation!.longitude?.toStringAsFixed(6) ?? '未知'),
            _buildLocationRow('精度', '${_currentLocation!.accuracy?.toStringAsFixed(1) ?? '未知'} 米'),
            _buildLocationRow('海拔', '${_currentLocation!.altitude?.toStringAsFixed(1) ?? '未知'} 米'),
            _buildLocationRow('速度', '${_currentLocation!.speed?.toStringAsFixed(1) ?? '未知'} 米/秒'),
            _buildLocationRow('方向', '${_currentLocation!.heading?.toStringAsFixed(1) ?? '未知'} 度'),
          ],
        ),
      ),
    );
  }

  Widget _buildLocationRow(String label, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(label),
          Text(value, style: const TextStyle(fontWeight: FontWeight.bold)),
        ],
      ),
    );
  }
}
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐