// 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 createState() => _MapWebViewPageState(); } class _MapWebViewPageState extends State { 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 _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 _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? _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 _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 _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 _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('地图页面销毁'); } }