Advertisement
altervisi0n

Untitled

May 12th, 2025
361
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Dart 5.65 KB | None | 0 0
  1.  
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_map/flutter_map.dart';
  4. import 'package:latlong2/latlong.dart';
  5. import 'package:viweather1/services/location_service.dart';
  6.  
  7. class WeatherMap extends StatefulWidget {
  8. final double latitude;
  9. final double longitude;
  10. final Function(double lat, double lon, LocationInfo locationInfo) onLocationSelected;
  11.  
  12. const WeatherMap({
  13. Key? key,
  14. required this.latitude,
  15. required this.longitude,
  16. required this.onLocationSelected,
  17. }) : super(key: key);
  18.  
  19. @override
  20. State<WeatherMap> createState() => _WeatherMapState();
  21. }
  22.  
  23. class _WeatherMapState extends State<WeatherMap> {
  24. late final MapController _mapController;
  25. final LocationService _locationService = LocationService();
  26. LatLng? _selectedLocation;
  27. LocationInfo? _currentLocationInfo;
  28. bool _isLoading = false;
  29. double _currentZoom = 10.0;
  30.  
  31. @override
  32. void initState() {
  33. super.initState();
  34. _mapController = MapController();
  35. _selectedLocation = LatLng(widget.latitude, widget.longitude);
  36. _updateLocationInfo();
  37. }
  38.  
  39. Future<void> _updateLocationInfo() async {
  40. if (_selectedLocation != null) {
  41. final locationInfo = await _locationService.getCityFromCoordinates(
  42. _selectedLocation!.latitude,
  43. _selectedLocation!.longitude,
  44. );
  45. setState(() {
  46. _currentLocationInfo = locationInfo;
  47. });
  48. }
  49. }
  50.  
  51. @override
  52. void didUpdateWidget(WeatherMap oldWidget) {
  53. super.didUpdateWidget(oldWidget);
  54. if (oldWidget.latitude != widget.latitude || oldWidget.longitude != widget.longitude) {
  55. _selectedLocation = LatLng(widget.latitude, widget.longitude);
  56. _mapController.move(
  57. _selectedLocation!,
  58. _currentZoom,
  59. );
  60. _updateLocationInfo();
  61. }
  62. }
  63.  
  64. void _handleZoomIn() {
  65. final newZoom = _currentZoom + 1;
  66. if (newZoom <= 18) { // Максимальный зум для OpenStreetMap
  67. setState(() {
  68. _currentZoom = newZoom;
  69. });
  70. _mapController.move(_selectedLocation!, _currentZoom);
  71. }
  72. }
  73.  
  74. void _handleZoomOut() {
  75. final newZoom = _currentZoom - 1;
  76. if (newZoom >= 3) { // Минимальный зум для комфортного просмотра
  77. setState(() {
  78. _currentZoom = newZoom;
  79. });
  80. _mapController.move(_selectedLocation!, _currentZoom);
  81. }
  82. }
  83.  
  84. Future<void> _handleTap(LatLng location) async {
  85. setState(() {
  86. _isLoading = true;
  87. _selectedLocation = location;
  88. });
  89.  
  90. try {
  91. final locationInfo = await _locationService.getCityFromCoordinates(
  92. location.latitude,
  93. location.longitude,
  94. );
  95. setState(() {
  96. _currentLocationInfo = locationInfo;
  97. });
  98. widget.onLocationSelected(location.latitude, location.longitude, locationInfo);
  99. } catch (e) {
  100. ScaffoldMessenger.of(context).showSnackBar(
  101. SnackBar(content: Text('Error selecting location: $e')),
  102. );
  103. } finally {
  104. setState(() {
  105. _isLoading = false;
  106. });
  107. }
  108. }
  109.  
  110. @override
  111. void dispose() {
  112. _mapController.dispose();
  113. super.dispose();
  114. }
  115.  
  116. @override
  117. Widget build(BuildContext context) {
  118. return Stack(
  119. children: [
  120. Container(
  121. height: 200,
  122. decoration: BoxDecoration(
  123. borderRadius: BorderRadius.circular(12),
  124. boxShadow: [
  125. BoxShadow(
  126. color: Colors.black.withOpacity(0.1),
  127. blurRadius: 10,
  128. offset: const Offset(0, 5),
  129. ),
  130. ],
  131. ),
  132. child: ClipRRect(
  133. borderRadius: BorderRadius.circular(12),
  134. child: FlutterMap(
  135. mapController: _mapController,
  136. options: MapOptions(
  137. center: _selectedLocation,
  138. zoom: _currentZoom,
  139. onTap: (_, location) => _handleTap(location),
  140. onMapReady: () {
  141. _mapController.move(_selectedLocation!, _currentZoom);
  142. },
  143. interactiveFlags: InteractiveFlag.all,
  144. enableScrollWheel: true,
  145. enableMultiFingerGestureRace: true,
  146. ),
  147. children: [
  148. TileLayer(
  149. urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
  150. userAgentPackageName: 'com.example.viweather1',
  151. ),
  152. MarkerLayer(
  153. markers: [
  154. if (_selectedLocation != null)
  155. Marker(
  156. point: _selectedLocation!,
  157. child: Container(
  158. width: 50,
  159. height: 50,
  160. decoration: BoxDecoration(
  161. color: Colors.white,
  162. borderRadius: BorderRadius.circular(25),
  163. boxShadow: [
  164. BoxShadow(
  165. color: Colors.black.withOpacity(0.2),
  166. blurRadius: 5,
  167. offset: const Offset(0, 2),
  168. ),
  169. ],
  170. ),
  171. child: const Icon(
  172. Icons.location_on,
  173. color: Colors.red,
  174. size: 30,
  175. ),
  176. ),
  177. ),
  178. ],
  179. ),
  180. ],
  181. ),
  182. ),
  183. ),
  184. // Location info overlay
  185. if (_currentLocationInfo != null)
  186. Positioned(
  187. left: 8,
  188. top: 8,
  189. child: Container(
  190. padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
  191. decoration: BoxDecoration(
  192. color: Colors.white.withOpacity(0.9),
  193. borderRadius: BorderRadius.circular(4),
  194. boxShadow: [
  195. BoxShadow(
  196. color: Colors.black.withOpacity(0.1),
  197. blurRadius: 4,
  198. offset: const Offset(0, 2),
  199. ),
  200. ],
  201. ),
  202. child: Text(
  203. '${_currentLocationInfo!.cityName}, ${_currentLocationInfo!.countryCode}',
  204. style: const TextStyle(
  205. fontSize: 12,
  206. fontWeight: FontWeight.w500,
  207. ),
  208. ),
  209. ),
  210. ),
  211. // Кнопки масштабирования
  212. Positioned(
  213. right: 8,
  214. top: 8,
  215. child: Column(
  216. children: [
  217. Container(
  218. decoration: BoxDecoration(
  219. color: Colors.white,
  220. borderRadius: BorderRadius.circular(4),
  221. boxShadow: [
  222. BoxShadow(
  223. color: Colors.black.withOpacity(0.1),
  224. blurRadius: 4,
  225. offset: const Offset(0, 2),
  226. ),
  227. ],
  228. ),
  229. child: Column(
  230. children: [
  231. IconButton(
  232.  
  233.  
  234. icon: const Icon(Icons.add, size: 20),
  235. onPressed: _handleZoomIn,
  236. padding: const EdgeInsets.all(8),
  237. constraints: const BoxConstraints(),
  238. color: Colors.black87,
  239. ),
  240. Container(
  241. height: 1,
  242. color: Colors.grey.withOpacity(0.3),
  243. ),
  244. IconButton(
  245. icon: const Icon(Icons.remove, size: 20),
  246. onPressed: _handleZoomOut,
  247. padding: const EdgeInsets.all(8),
  248. constraints: const BoxConstraints(),
  249. color: Colors.black87,
  250. ),
  251. ],
  252. ),
  253. ),
  254. ],
  255. ),
  256. ),
  257. if (_isLoading)
  258. Container(
  259. height: 200,
  260. decoration: BoxDecoration(
  261. borderRadius: BorderRadius.circular(12),
  262. color: Colors.black.withOpacity(0.3),
  263. ),
  264. child: const Center(
  265. child: CircularProgressIndicator(),
  266. ),
  267. ),
  268. ],
  269. );
  270. }
  271. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement