179 lines
5.4 KiB
Dart
179 lines
5.4 KiB
Dart
import 'dart:async';
|
||
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_map/flutter_map.dart';
|
||
import 'package:latlong2/latlong.dart';
|
||
import 'package:geolocator/geolocator.dart';
|
||
import 'package:permission_handler/permission_handler.dart';
|
||
import 'package:maibu_satabot_v2/core/localization/app_localizations.dart';
|
||
|
||
import 'CoordTransform/coord_transform.dart';
|
||
|
||
class AmapFlutterMapPage extends StatefulWidget {
|
||
const AmapFlutterMapPage({super.key});
|
||
|
||
@override
|
||
State<AmapFlutterMapPage> createState() => _AmapFlutterMapPageState();
|
||
}
|
||
|
||
class _AmapFlutterMapPageState extends State<AmapFlutterMapPage> {
|
||
final MapController _mapController = MapController();
|
||
static const String amapKey = "bbb1f0f20eed6bf679eddf2625630aba";
|
||
|
||
LatLng _currentCenter = const LatLng(39.908823, 116.397470);
|
||
bool _isLoading = true;
|
||
// 新增:标记页面是否已销毁,防止异步操作访问已释放的控制器
|
||
bool _isDisposed = false;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||
_checkAndGetLocation();
|
||
});
|
||
}
|
||
|
||
// 新增:页面销毁时标记状态
|
||
@override
|
||
void dispose() {
|
||
_isDisposed = true; // 先标记销毁状态
|
||
// _mapController.dispose(); // 再销毁控制器
|
||
super.dispose();
|
||
}
|
||
|
||
Future<void> _checkAndGetLocation() async {
|
||
var status = await Permission.location.status;
|
||
if (status.isDenied) {
|
||
status = await Permission.location.request();
|
||
if (status.isDenied) {
|
||
if (!_isDisposed) { // 防护:页面没销毁才弹提示
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
SnackBar(content: Text(AppLocalizations.of(context).translate('route_planning.location_permission_denied'))),
|
||
);
|
||
setState(() { _isLoading = false; });
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (status.isPermanentlyDenied) {
|
||
if (!_isDisposed) { // 防护:页面没销毁才弹提示
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
SnackBar(
|
||
content: Text(AppLocalizations.of(context).translate('route_planning.location_permanently_denied')),
|
||
action: SnackBarAction(label: AppLocalizations.of(context).translate('route_planning.go_to_settings'), onPressed: openAppSettings),
|
||
),
|
||
);
|
||
setState(() { _isLoading = false; });
|
||
}
|
||
return;
|
||
}
|
||
|
||
await _getCurrentLocation();
|
||
}
|
||
|
||
Future<void> _getCurrentLocation() async {
|
||
if (_isDisposed) return;
|
||
|
||
setState(() { _isLoading = true; });
|
||
print('📍 开始定位...');
|
||
|
||
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||
if (!serviceEnabled) {
|
||
if (!_isDisposed) {
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
SnackBar(content: Text(AppLocalizations.of(context).translate('route_planning.location_service_disabled'))),
|
||
);
|
||
setState(() { _isLoading = false; });
|
||
}
|
||
return;
|
||
}
|
||
|
||
try {
|
||
final position = await Geolocator.getCurrentPosition(
|
||
desiredAccuracy: LocationAccuracy.bestForNavigation,
|
||
forceAndroidLocationManager: true,
|
||
timeLimit: const Duration(seconds: 8),
|
||
);
|
||
|
||
if (_isDisposed) return;
|
||
|
||
// ⭐⭐⭐ 关键修复点:WGS84 → GCJ02 ⭐⭐⭐
|
||
final gcjPoint = CoordTransform.wgs84ToGcj02(
|
||
position.latitude,
|
||
position.longitude,
|
||
);
|
||
|
||
setState(() {
|
||
_currentCenter = gcjPoint;
|
||
_isLoading = false;
|
||
});
|
||
|
||
if (!_isDisposed) {
|
||
_mapController.move(gcjPoint, 15.0);
|
||
}
|
||
|
||
print('✅ 定位成功(GCJ02): ${gcjPoint.latitude}, ${gcjPoint.longitude}');
|
||
} catch (e) {
|
||
if (!_isDisposed) {
|
||
setState(() { _isLoading = false; });
|
||
ScaffoldMessenger.of(context).showSnackBar(
|
||
SnackBar(
|
||
content: Text('${AppLocalizations.of(context).translate('route_planning.location_failed')}: $e'),
|
||
action: SnackBarAction(
|
||
label: AppLocalizations.of(context).translate('route_planning.retry'),
|
||
onPressed: _checkAndGetLocation,
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
appBar: AppBar(
|
||
title: Text(AppLocalizations.of(context).translate('route_planning.title')),
|
||
actions: [
|
||
IconButton(
|
||
icon: const Icon(Icons.my_location),
|
||
onPressed: _checkAndGetLocation,
|
||
tooltip: AppLocalizations.of(context).translate('route_planning.my_location'),
|
||
),
|
||
],
|
||
),
|
||
body: _isLoading
|
||
? const Center(child: CircularProgressIndicator())
|
||
: FlutterMap(
|
||
mapController: _mapController,
|
||
options: MapOptions(
|
||
center: _currentCenter,
|
||
zoom: 15.0,
|
||
minZoom: 3.0,
|
||
maxZoom: 18.0,
|
||
interactiveFlags: InteractiveFlag.all,
|
||
),
|
||
children: [
|
||
TileLayer(
|
||
urlTemplate:
|
||
"https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}&key=$amapKey",
|
||
subdomains: const ['1', '2', '3', '4'],
|
||
tileProvider: NetworkTileProvider(),
|
||
),
|
||
MarkerLayer(
|
||
markers: [
|
||
Marker(
|
||
point: _currentCenter,
|
||
width: 40,
|
||
height: 40,
|
||
child: const Icon(Icons.location_on, color: Colors.red, size: 40),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
} |