import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; import 'package:geolocator/geolocator.dart'; import '../constants.dart'; import '../providers.dart'; import '../providers/main_screen_provider.dart'; import '../tools/app_ads_managers.dart'; class MapScreen extends ConsumerStatefulWidget { const MapScreen({super.key}); @override ConsumerState createState() => _MapScreenState(); } class _MapScreenState extends ConsumerState with SingleTickerProviderStateMixin { final MapController _mapController = MapController(); LatLng _selectedLocation = const LatLng( 40.7128, -74.0060, ); // Default: New York bool _isLoading = false; bool _isLocating = false; late AnimationController _pulseController; late Animation _pulseAnimation; @override void initState() { super.initState(); _pulseController = AnimationController( vsync: this, duration: const Duration(milliseconds: 1500), )..repeat(reverse: true); _pulseAnimation = Tween(begin: 0.8, end: 1.2).animate( CurvedAnimation(parent: _pulseController, curve: Curves.easeInOut), ); // Initialize map to New York WidgetsBinding.instance.addPostFrameCallback((_) { _mapController.move(_selectedLocation, 10.0); }); } Future _getCurrentLocation() async { setState(() { _isLocating = true; }); try { // Check if location services are enabled bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text( 'Location services are disabled. Please enable them in settings.', ), backgroundColor: Colors.orange, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ); } return; } // Check location permissions LocationPermission permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('Location permissions are denied.'), backgroundColor: Colors.orange, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ); } return; } } if (permission == LocationPermission.deniedForever) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text( 'Location permissions are permanently denied. Please enable them in app settings.', ), backgroundColor: Colors.orange, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ); } return; } // Get current position Position position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.high, ); final newLocation = LatLng(position.latitude, position.longitude); setState(() { _selectedLocation = newLocation; }); // Animate map to current location _mapController.move(newLocation, 15.0); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Row( children: [ Icon(Icons.my_location, color: Colors.white, size: 20), SizedBox(width: 8), Text('Location updated to current position'), ], ), backgroundColor: Colors.green, duration: const Duration(seconds: 2), behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Failed to get location: $e'), backgroundColor: Colors.red, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ); } } finally { if (mounted) { setState(() { _isLocating = false; }); } } } void _onMapMoved() { final center = _mapController.camera.center; setState(() { _selectedLocation = center; }); } Future _confirmLocation() async { InterstitialAdManager.instance.showAd(InterstitialAdType.second,onAdCompleted: (){ }); setState(() { _isLoading = true; }); try { final coordinateQuery = '${_selectedLocation.latitude},${_selectedLocation.longitude}'; final weatherNotifier = ref.read(weatherProvider.notifier); await weatherNotifier.switchCity(coordinateQuery); if (mounted) { switchToHome(ref); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Row( children: [ Icon(Icons.check_circle, color: Colors.white), SizedBox(width: 8), Text('Weather information retrieved successfully'), ], ), backgroundColor: Colors.green, duration: const Duration(seconds: 2), behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Failed to fetch weather: $e'), backgroundColor: Colors.red, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ); } } finally { if (mounted) { setState(() { _isLoading = false; }); } } } @override Widget build(BuildContext context) { return Scaffold( body: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ kDarkPurple, kDarkPurple.withOpacity(0.95), Colors.black.withOpacity(0.9), ], stops: const [0.0, 0.5, 1.0], ), ), child: Stack( children: [ FlutterMap( mapController: _mapController, options: MapOptions( initialCenter: _selectedLocation, initialZoom: 10.0, onMapEvent: (event) { if (event is MapEventMove) { _onMapMoved(); } }, ), children: [ TileLayer( urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', userAgentPackageName: 'com.example.atmo_sphere', ), ], ), // Center location pin with pulse animation Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ AnimatedBuilder( animation: _pulseAnimation, builder: (context, child) { return Transform.scale( scale: _pulseAnimation.value, child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.red.withOpacity(0.4), blurRadius: 20, spreadRadius: 5, ), BoxShadow( color: Colors.black.withOpacity(0.3), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: const Icon( Icons.location_on, color: Colors.red, size: 48, ), ), ); }, ), const SizedBox(height: 12), Container( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 10, ), decoration: BoxDecoration( gradient: LinearGradient( colors: [ Colors.black.withOpacity(0.9), Colors.black.withOpacity(0.8), ], ), borderRadius: BorderRadius.circular(20), border: Border.all( color: Colors.white.withOpacity(0.2), width: 1, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.4), blurRadius: 15, offset: const Offset(0, 4), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.my_location, size: 14, color: Colors.blueAccent.withOpacity(0.9), ), const SizedBox(width: 6), Text( 'Coordinates', style: TextStyle( color: Colors.white.withOpacity(0.7), fontSize: 10, fontWeight: FontWeight.w500, letterSpacing: 0.5, ), ), ], ), const SizedBox(height: 4), Text( '${_selectedLocation.latitude.toStringAsFixed(4)}, ${_selectedLocation.longitude.toStringAsFixed(4)}', style: const TextStyle( color: Colors.white, fontSize: 13, fontWeight: FontWeight.w700, letterSpacing: 0.5, fontFeatures: [FontFeature.tabularFigures()], ), ), ], ), ), ], ), ), // Location button (top right) Positioned( top: MediaQuery.of(context).padding.top + 20, right: 16, child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.greenAccent.withOpacity(0.4), blurRadius: 15, spreadRadius: 2, ), BoxShadow( color: Colors.black.withOpacity(0.3), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(16), onTap: _isLocating ? null : _getCurrentLocation, child: Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( gradient: LinearGradient( colors: [ Colors.greenAccent.withOpacity(0.9), Colors.greenAccent.withOpacity(0.8), ], ), borderRadius: BorderRadius.circular(16), border: Border.all( color: Colors.white.withOpacity(0.2), width: 1, ), ), child: _isLocating ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2.5, valueColor: AlwaysStoppedAnimation( Colors.white, ), ), ) : const Icon( Icons.my_location, color: Colors.white, size: 24, ), ), ), ), ), ), // Top info card Positioned( top: MediaQuery.of(context).padding.top + 20, left: 16, right: 80, // Leave space for location button child: Container( padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 16, ), decoration: BoxDecoration( gradient: LinearGradient( colors: [ Colors.black.withOpacity(0.85), Colors.black.withOpacity(0.75), ], ), borderRadius: BorderRadius.circular(20), border: Border.all( color: Colors.white.withOpacity(0.1), width: 1, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.4), blurRadius: 20, offset: const Offset(0, 6), ), ], ), child: Row( children: [ Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.blueAccent.withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: const Icon( Icons.info_outline, color: Colors.blueAccent, size: 22, ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ const Text( 'Select Location', style: TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold, letterSpacing: 0.3, ), ), const SizedBox(height: 4), Text( 'Drag the map to choose a location, then tap confirm to get weather', style: TextStyle( color: Colors.white.withOpacity(0.8), fontSize: 13, fontWeight: FontWeight.w500, height: 1.3, ), ), ], ), ), ], ), ), ), // Bottom confirm button Positioned( bottom: MediaQuery.of(context).padding.bottom + 24, left: 16, right: 16, child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.blueAccent.withOpacity(0.4), blurRadius: 20, spreadRadius: 2, ), BoxShadow( color: Colors.black.withOpacity(0.3), blurRadius: 15, offset: const Offset(0, 6), ), ], ), child: ElevatedButton( onPressed: _isLoading ? null : _confirmLocation, style: ElevatedButton.styleFrom( backgroundColor: Colors.blueAccent, foregroundColor: Colors.white, disabledBackgroundColor: Colors.blueAccent.withOpacity(0.6), padding: const EdgeInsets.symmetric( horizontal: 32, vertical: 18, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), elevation: 0, ), child: _isLoading ? const SizedBox( width: 24, height: 24, child: CircularProgressIndicator( strokeWidth: 2.5, valueColor: AlwaysStoppedAnimation( Colors.white, ), ), ) : const Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.check_circle_outline, size: 26), SizedBox(width: 10), Text( 'Confirm', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, letterSpacing: 0.8, ), ), ], ), ), ), ), ], ), ), ); } @override void dispose() { _pulseController.dispose(); _mapController.dispose(); super.dispose(); } }