import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; import 'package:geolocator/geolocator.dart'; import 'package:geocoding/geocoding.dart'; class MapPage extends StatefulWidget { const MapPage({super.key}); @override State createState() => _MapPageState(); } class _MapPageState extends State { // 默认中心点(北京) LatLng _currentPosition = const LatLng(39.9042, 116.4074); String _address = "正在加载地址..."; bool _isLoading = false; // 地图控制器 final MapController _mapController = MapController(); @override void initState() { super.initState(); _getCurrentLocation(); } // 1. 获取当前位置并解析地址 Future _getCurrentLocation() async { setState(() { _isLoading = true; _address = "正在检查权限..."; }); try { // 1. 检查服务是否开启 bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { setState(() { _address = "请在系统设置中开启‘定位服务’总开关"; _isLoading = false; }); return; } // 2. 检查权限 LocationPermission permission = await Geolocator.checkPermission(); // 如果权限被拒绝,尝试请求 if (permission == LocationPermission.denied) { setState(() { _address = "正在请求权限..."; }); // 🔑 注意:macOS 上这行可能不会弹窗,而是直接返回 denied permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { setState(() { _address = "权限被拒绝。请去【系统设置->隐私->定位】中手动开启本App权限。"; _isLoading = false; }); return; } } if (permission == LocationPermission.deniedForever) { setState(() { _address = "权限被永久拒绝,请在系统设置中手动开启。"; _isLoading = false; }); return; } // 3. 获取位置 setState(() { _address = "正在获取坐标..."; }); // 🔑 添加超时限制,防止卡死 Position position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.high, timeLimit: const Duration(seconds: 15), ).timeout( const Duration(seconds: 15), onTimeout: () { throw Exception("定位超时,请检查网络连接或移动到有信号的地方"); }, ); LatLng newPos = LatLng(position.latitude, position.longitude); _mapController.move(newPos, 15.0); setState(() { _currentPosition = newPos; }); await _getAddressFromLatLng(newPos); } catch (e) { debugPrint("定位错误详情: $e"); setState(() { _address = "定位失败: ${e.toString()}"; _isLoading = false; }); } } // 2. 逆地理编码:经纬度 -> 文字地址 Future _getAddressFromLatLng(LatLng position) async { try { List placemarks = await placemarkFromCoordinates( position.latitude, position.longitude, ); if (placemarks.isNotEmpty) { Placemark place = placemarks[0]; setState(() { // 组合地址:省 + 市 + 区 + 街道 _address = "${place.street ?? ""}, ${place.locality ?? ""}, ${place.administrativeArea ?? ""}"; }); } } catch (e) { setState(() { _address = "地址解析失败"; }); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.transparent, // 保持透明,透出玻璃效果 body: Stack( children: [ // --- 底层:地图 --- // --- 底层:地图 --- FlutterMap( mapController: _mapController, options: MapOptions( initialCenter: _currentPosition, initialZoom: 13.0, // 🔑 添加交互标志,确保地图能响应鼠标拖拽 interactionOptions: const InteractionOptions( flags: InteractiveFlag.all, ), ), children: [ TileLayer( // ✅ 替换为 CartoDB 浅色主题(速度快,无需Key) urlTemplate: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', subdomains: ['a', 'b', 'c', 'd'], userAgentPackageName: 'com.example.app', // 🔑 添加错误监听,看看是不是还有网络问题 errorTileCallback: (tile, error, stackTrace) { debugPrint('地图瓦片加载错误: $error'); }, ), MarkerLayer( markers: [ Marker( width: 80.0, height: 80.0, point: _currentPosition, child: const Icon( Icons.location_pin, color: Colors.red, size: 40, ), ), ], ), ], ), // --- 顶层:UI 控件 --- // 1. 顶部地址卡片 (玻璃风格) Positioned( top: 50, left: 20, right: 20, child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white.withOpacity(0.85), // 半透明白 borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 10, offset: const Offset(0, 5), ), ], ), child: Row( children: [ const Icon(Icons.place, color: Colors.blueAccent), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "当前位置", style: TextStyle(fontSize: 12, color: Colors.grey), ), const SizedBox(height: 4), Text( _address, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ), if (_isLoading) const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ), ], ), ), ), // 2. 右下角定位按钮 Positioned( bottom: 100, // 留出底部导航栏的空间 right: 20, child: FloatingActionButton( onPressed: _getCurrentLocation, backgroundColor: Colors.white, child: const Icon(Icons.my_location, color: Colors.blue), ), ), ], ), ); } }