QinGang_interested/lib/customWidget/BaiDuMap/map_webview_page.dart

488 lines
15 KiB
Dart
Raw Permalink Normal View History

2025-12-12 09:11:30 +08:00
// lib/pages/map_webview_page.dart
import 'dart:async';
import 'dart:convert';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:webview_flutter/webview_flutter.dart';
class MapWebViewPage extends StatefulWidget {
const MapWebViewPage({this.canEdit = true,this.oldLongitude = '',this.oldLatitude = '', Key? key}) : super(key: key);
final bool canEdit;
final String oldLongitude;
final String oldLatitude;
@override
State<MapWebViewPage> createState() => _MapWebViewPageState();
}
class _MapWebViewPageState extends State<MapWebViewPage> {
late final WebViewController _controller;
bool _loading = true;
String _mapUrl = '';
double? _selectedLongitude;
double? _selectedLatitude;
bool _locationError = false;
// 默认坐标(北京)
static const double defaultLongitude = 116.397428;
static const double defaultLatitude = 39.90923;
@override
void initState() {
super.initState();
_initializeWebView();
_initializeMap();
}
void _initializeWebView() {
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel('FlutterChannel', onMessageReceived: _onMessageReceived)
..setNavigationDelegate(NavigationDelegate(
onPageStarted: (url) {
debugPrint('页面开始加载: $url');
},
onPageFinished: (url) {
debugPrint('页面加载完成: $url');
setState(() {
_loading = false;
});
_injectFlutterBridge();
},
onWebResourceError: (error) {
debugPrint('Web资源错误: ${error.errorCode} - ${error.description}');
setState(() {
_loading = false;
});
},
onNavigationRequest: (request) {
debugPrint('导航请求: ${request.url}');
return NavigationDecision.navigate;
},
));
}
Future<void> _initializeMap() async {
try {
debugPrint('开始获取位置信息...');
// 先设置默认URL避免长时间等待
final defaultCoord = _gcj02ToBd09(defaultLongitude, defaultLatitude);
_setMapUrl(defaultCoord[0], defaultCoord[1]);
var position ;
if(widget.canEdit){
// 异步获取当前位置
position = await _getCurrentLocationWithTimeout();
}else{
try{
double oldLongitude = double.parse(widget.oldLongitude);
double oldLatitude = double.parse(widget.oldLatitude);
position = Position(longitude: oldLongitude, latitude:oldLatitude ,
timestamp: DateTime.now(), accuracy: 0.0, altitude: 0.0, altitudeAccuracy: 0.0, heading: 0.0,
headingAccuracy: 0.0, speed: 0.0, speedAccuracy: 0.0);
}catch (e) {
// 异步获取当前位置
position = await _getCurrentLocationWithTimeout();
}
}
if (position != null) {
debugPrint('成功获取位置: ${position.longitude}, ${position.latitude}');
// GCJ02 -> BD09 坐标转换
final bd09Coord = _gcj02ToBd09(position.longitude, position.latitude);
_setMapUrl(bd09Coord[0], bd09Coord[1]);
} else {
debugPrint('使用默认位置');
_showToast('使用默认位置,您可以手动选择位置');
}
// 加载地图
await _controller.loadRequest(Uri.parse(_mapUrl));
} catch (e) {
debugPrint('地图初始化失败: $e');
// 即使出错也继续加载地图,使用默认位置
_showToast('位置获取失败,使用默认位置');
await _controller.loadRequest(Uri.parse(_mapUrl));
}
}
Future<Position?> _getCurrentLocationWithTimeout() async {
try {
// 首先检查定位服务是否开启
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
debugPrint('定位服务未开启');
_showToast('定位服务未开启,使用默认位置');
return null;
}
// 检查权限
LocationPermission permission = await Geolocator.checkPermission();
debugPrint('当前定位权限: $permission');
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
debugPrint('请求后定位权限: $permission');
}
if (permission == LocationPermission.denied ||
permission == LocationPermission.deniedForever) {
debugPrint('定位权限被拒绝');
_showToast('定位权限被拒绝,使用默认位置');
return null;
}
// 设置超时
final position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best,
).timeout(const Duration(seconds: 10));
return position;
} catch (e) {
debugPrint('获取位置异常: $e');
// 尝试获取最后已知位置
try {
final lastPosition = await Geolocator.getLastKnownPosition();
if (lastPosition != null) {
debugPrint('使用最后已知位置: ${lastPosition.longitude}, ${lastPosition.latitude}');
return lastPosition;
}
} catch (e) {
debugPrint('获取最后已知位置失败: $e');
}
return null;
}
}
void _setMapUrl(double longitude, double latitude) {
final timestamp = DateTime.now().millisecondsSinceEpoch;
setState(() {
_mapUrl = 'https://skqhdg.porthebei.com:9004/map/map.html?'
'longitude=$longitude&'
'latitude=$latitude&'
't=$timestamp';
});
debugPrint('地图URL: $_mapUrl');
}
void _injectFlutterBridge() {
const bridgeScript = '''
// 重写uni.postMessage以发送到Flutter
if (typeof uni !== 'undefined') {
const originalPostMessage = uni.postMessage;
uni.postMessage = function(data) {
console.log('uni.postMessage被调用:', data);
// 发送到Flutter
if (window.FlutterChannel) {
try {
window.FlutterChannel.postMessage(JSON.stringify(data));
} catch(e) {
console.log('发送到Flutter失败:', e);
}
}
// 保持原有逻辑
if (typeof originalPostMessage === 'function') {
originalPostMessage(data);
}
};
// 确保plus环境检测返回true
uni.getEnv = function(callback) {
if (typeof callback === 'function') {
callback({ plus: true });
}
};
}
// 添加Flutter专用的消息发送方法
window.sendToFlutter = function(data) {
console.log('sendToFlutter被调用:', data);
if (window.FlutterChannel) {
try {
window.FlutterChannel.postMessage(JSON.stringify(data));
} catch(e) {
console.log('发送到Flutter失败:', e);
}
}
};
console.log('Flutter bridge注入完成');
''';
_controller.runJavaScript(bridgeScript).catchError((error) {
debugPrint('注入Flutter bridge失败: $error');
});
}
void _onMessageReceived(JavaScriptMessage message) {
try {
debugPrint('收到原始消息: ${message.message}');
final data = jsonDecode(message.message);
debugPrint('解析后的消息: $data');
// 解析坐标数据
if (data != null) {
final coords = data['data'];
setState(() {
_selectedLongitude = coords['longitue'];
_selectedLatitude = coords['latitude'];
});
debugPrint('选中坐标: $_selectedLongitude, $_selectedLatitude');
} else {
debugPrint('无法从消息中提取坐标');
}
} catch (e) {
debugPrint('解析地图消息失败: $e');
debugPrint('原始消息内容: ${message.message}');
}
}
Map<String, double>? _extractCoordinates(dynamic data) {
try {
debugPrint('开始提取坐标,数据类型: ${data.runtimeType}');
if (data is Map) {
// 处理不同的数据结构
dynamic coordsData = data;
// 处理嵌套结构
if (data.containsKey('data') && data['data'] is List && data['data'].isNotEmpty) {
coordsData = data['data'][0];
debugPrint('从data数组中提取坐标数据');
}
if (coordsData is Map) {
debugPrint('坐标数据键: ${coordsData.keys}');
// 处理拼写错误 (longitue -> longitude)
final longitude = _toDouble(coordsData['longitude']) ??
_toDouble(coordsData['longitue']);
final latitude = _toDouble(coordsData['latitude']);
debugPrint('解析结果 - 经度: $longitude, 纬度: $latitude');
if (longitude != null && latitude != null) {
return {
'longitude': longitude,
'latitude': latitude,
};
}
}
} else if (data is String) {
// 尝试从字符串中提取坐标
debugPrint('尝试从字符串中提取坐标');
final coordPattern = RegExp(r'[-+]?\d+\.\d+');
final matches = coordPattern.allMatches(data).toList();
if (matches.length >= 2) {
final longitude = double.tryParse(matches[0].group(0)!);
final latitude = double.tryParse(matches[1].group(0)!);
if (longitude != null && latitude != null) {
return {
'longitude': longitude,
'latitude': latitude,
};
}
}
}
} catch (e) {
debugPrint('提取坐标失败: $e');
}
return null;
}
double? _toDouble(dynamic value) {
if (value == null) return null;
if (value is double) return value;
if (value is int) return value.toDouble();
if (value is String) {
// 处理可能的字符串格式
final cleaned = value.replaceAll(RegExp(r'[^\d.-]'), '');
return double.tryParse(cleaned);
}
return null;
}
void _showToast(String message) {
if (!mounted) return;
ToastUtil.showNormal(context, message);
}
Future<void> _confirmSelection() async {
if (_selectedLongitude == null || _selectedLatitude == null) {
// 如果没有选中位置,尝试从页面获取当前位置
await _getCurrentLocationFromPage();
}
if (_selectedLongitude != null && _selectedLatitude != null) {
final result = {
'longitude': _selectedLongitude!,
'latitude': _selectedLatitude!,
};
debugPrint('返回坐标结果: $result');
Navigator.of(context).pop(result);
} else {
_showToast('请先在地图上选择位置');
}
}
Future<void> _getCurrentLocationFromPage() async {
try {
debugPrint('尝试从页面获取选中位置');
const getterScript = '''
(function(){
try {
// 尝试多种方式获取选中位置
if (typeof getSelectedLocation === 'function') {
var result = getSelectedLocation();
if (result) return JSON.stringify(result);
}
if (window.selectedLocation) {
return JSON.stringify(window.selectedLocation);
}
// 如果没有选中位置,返回地图中心
if (map && typeof map.getCenter === 'function') {
var center = map.getCenter();
return JSON.stringify({
longitude: center.getLng(),
latitude: center.getLat()
});
}
return null;
} catch(e) {
console.log('获取位置错误:', e);
return null;
}
})();
''';
final result = await _controller.runJavaScriptReturningResult(getterScript);
debugPrint('页面返回的位置结果: $result');
if (result != null) {
String resultString = result.toString();
// 处理可能的双重编码
if (resultString.startsWith('"') && resultString.endsWith('"')) {
resultString = resultString.substring(1, resultString.length - 1);
}
final coords = _extractCoordinates(jsonDecode(resultString));
if (coords != null) {
setState(() {
_selectedLongitude = coords['longitude'];
_selectedLatitude = coords['latitude'];
});
debugPrint('从页面获取到坐标: $_selectedLongitude, $_selectedLatitude');
}
}
} catch (e) {
debugPrint('从页面获取位置失败: $e');
}
}
void _retryLocation() async {
setState(() {
_loading = true;
_locationError = false;
});
await _initializeMap();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppbar(
title: '地图选点',
actions: [
if (_selectedLongitude != null&&widget.canEdit)
TextButton(
onPressed: _confirmSelection,
child: const Text(
'确定',
style: TextStyle(color: Colors.white, fontSize: 17),
),
),
],
),
body: Column(
children: [
// 状态提示栏
if (_locationError)
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
color: Colors.orange[100],
child: Row(
children: [
Icon(Icons.warning_amber, color: Colors.orange[800], size: 16),
const SizedBox(width: 8),
Expanded(
child: Text(
'定位失败,使用默认位置',
style: TextStyle(color: Colors.orange[800], fontSize: 12),
),
),
TextButton(
onPressed: _retryLocation,
child: Text(
'重试',
style: TextStyle(color: Colors.orange[800], fontSize: 12),
),
),
],
),
),
// 地图区域
Expanded(
child: Stack(
children: [
if (_mapUrl.isNotEmpty)
WebViewWidget(controller: _controller),
if (_loading)
const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('地图加载中...'),
],
),
),
],
),
),
],
),
);
}
// GCJ02 -> BD09 坐标转换
List<double> _gcj02ToBd09(double lng, double lat) {
const double xPi = math.pi * 3000.0 / 180.0;
final double z = math.sqrt(lng * lng + lat * lat) + 0.00002 * math.sin(lat * xPi);
final double theta = math.atan2(lat, lng) + 0.000003 * math.cos(lng * xPi);
final double bdLng = z * math.cos(theta) + 0.0065;
final double bdLat = z * math.sin(theta) + 0.006;
return [bdLng, bdLat];
}
@override
void dispose() {
super.dispose();
debugPrint('地图页面销毁');
}
}