如何在Flutter中实现指向指定位置而非正北的指南针?
实现指向特定经纬度的指南针页面
需求说明
- 指南针需指向目标位置:
LatLng(50.074743304804485, 8.238497357816172) - 站在目标位置北侧时,指南针指针在屏幕上向下
- 站在目标位置南侧时,指南针指针在屏幕上向上
现有代码
import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_compass/flutter_compass.dart'; class CompassPage extends StatefulWidget { const CompassPage({Key? key}) : super(key: key); @override State<CompassPage> createState() => _CompassPageState(); } class _CompassPageState extends State<CompassPage> { double? heading = 0; @override void initState() { super.initState(); FlutterCompass.events!.listen((event) { setState(() { heading = event.heading; }); heading = event.heading; }); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.orange, centerTitle: true, title: const Text("Compass Page"), ), body: Center( child: Padding( padding: const EdgeInsets.all(20), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( "${heading!.ceil()} degrees", style: const TextStyle( color: Colors.white, fontSize: 26, fontWeight: FontWeight.bold, ), ), Expanded( child: Stack( alignment: Alignment.center, children: [ Image.asset( "assets/images/cadrant.png", scale: 1.1, ), Transform.rotate( angle: (heading! * (pi / 100) * -1), child: Image.asset("assets/images/compass.png"), ), ], ), ), ], ), ), ), ); } }
实现方案
1. 添加位置服务依赖
在pubspec.yaml中添加位置服务依赖:
dependencies: flutter: sdk: flutter flutter_compass: ^0.7.0 geolocator: ^10.1.0
2. 配置位置权限
- Android:在
android/app/src/main/AndroidManifest.xml中添加权限:<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> - iOS:在
ios/Runner/Info.plist中添加权限描述:<key>NSLocationWhenInUseUsageDescription</key> <string>需要获取您的位置来指向目标地点</string>
3. 完整代码实现
整合位置获取、方位角计算和指南针指向逻辑的最终代码:
import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_compass/flutter_compass.dart'; import 'package:geolocator/geolocator.dart'; class CompassPage extends StatefulWidget { const CompassPage({Key? key}) : super(key: key); @override State<CompassPage> createState() => _CompassPageState(); } class _CompassPageState extends State<CompassPage> { // 目标位置 final LatLng _targetLocation = const LatLng(50.074743304804485, 8.238497357816172); double? _currentHeading; // 设备朝向正北的角度 Position? _currentPosition; // 当前位置 double? _targetBearing; // 从当前位置指向目标的方位角 @override void initState() { super.initState(); _initCompass(); _initLocation(); } // 初始化指南针监听 void _initCompass() { FlutterCompass.events!.listen((event) { setState(() { _currentHeading = event.heading; _calculateTargetBearing(); }); }); } // 初始化位置服务并获取当前位置 Future<void> _initLocation() async { // 请求位置权限 LocationPermission permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) return; } // 获取当前位置 _currentPosition = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high); // 监听位置变化 Geolocator.getPositionStream().listen((Position position) { setState(() { _currentPosition = position; _calculateTargetBearing(); }); }); } // 计算从当前位置到目标位置的方位角(0°=正北,顺时针递增) void _calculateTargetBearing() { if (_currentPosition == null) return; final lat1 = _currentPosition!.latitude * pi / 180; final lon1 = _currentPosition!.longitude * pi / 180; final lat2 = _targetLocation.latitude * pi / 180; final lon2 = _targetLocation.longitude * pi / 180; final dLon = lon2 - lon1; final y = sin(dLon) * cos(lat2); final x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon); double bearing = atan2(y, x) * 180 / pi; // 将方位角转换为0-360°范围 bearing = (bearing + 360) % 360; setState(() { _targetBearing = bearing; }); } // 计算指针需要旋转的角度 double _getPointerRotation() { if (_currentHeading == null || _targetBearing == null) return 0; // 核心逻辑:调整角度满足北侧指针向下、南侧指针向上的需求 double rotation = (_targetBearing! - _currentHeading! + 180) % 360; // 转换为弧度并反转旋转方向(适配Transform.rotate的坐标系) return -rotation * pi / 180; } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.orange, centerTitle: true, title: const Text("Compass Page"), ), body: Center( child: Padding( padding: const EdgeInsets.all(20), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ // 显示当前朝向和目标方位角 Text( _currentHeading == null || _targetBearing == null ? "获取位置中..." : "朝向: ${_currentHeading!.ceil()}° | 目标方位: ${_targetBearing!.ceil()}°", style: const TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, ), const SizedBox(height: 20), Expanded( child: Stack( alignment: Alignment.center, children: [ Image.asset( "assets/images/cadrant.png", scale: 1.1, ), Transform.rotate( angle: _getPointerRotation(), child: Image.asset("assets/images/compass.png"), ), ], ), ), ], ), ), ), ); } }
关键逻辑说明
- 方位角计算:通过球面三角学公式计算当前位置到目标位置的方位角,确保角度准确对应正北方向的顺时针偏移量。
- 指针旋转逻辑:通过
目标方位角 - 当前设备朝向 + 180°的计算,实现“北侧指针向下、南侧指针向上”的需求,同时适配Flutter的旋转坐标系。 - 实时更新:同时监听指南针朝向和位置变化,确保指针始终指向目标位置。
内容的提问来源于stack exchange,提问作者OverdoseB12
相关产品推荐
相关产品推荐

