diff --git a/lib/screens/mains/hunt/hunt.dart b/lib/screens/mains/hunt/hunt.dart index b042bcd..0f6f926 100644 --- a/lib/screens/mains/hunt/hunt.dart +++ b/lib/screens/mains/hunt/hunt.dart @@ -151,14 +151,14 @@ class _HuntContentState extends State<_HuntContent> with TickerProviderStateMixi const Color(0xFF0F172A), (cos(t * pi * 2 + pi) + 1) / 2)!, ] : [ - Color.lerp(const Color(0xFF84CEEB), - const Color(0xFF5680E9), (sin(t * pi * 2) + 1) / 2)!, - Color.lerp(const Color(0xFF5680E9), - const Color(0xFF8860D0), (cos(t * pi * 2 + pi / 3) + 1) / 2)!, - Color.lerp(const Color(0xFF8860D0), - const Color(0xFF5AB9EA), (sin(t * pi * 2 + pi / 2) + 1) / 2)!, - Color.lerp(const Color(0xFF5AB9EA), - const Color(0xFF84CEEB), (cos(t * pi * 2 + pi) + 1) / 2)!, + Color.lerp(const Color(0xFFFFFFFF), + const Color(0xFFF8F9FA), (sin(t * pi * 2) + 1) / 2)!, + Color.lerp(const Color(0xFFF8F9FA), + const Color(0xFFE9ECEF), (cos(t * pi * 2 + pi / 3) + 1) / 2)!, + Color.lerp(const Color(0xFFE9ECEF), + const Color(0xFFF8F9FA), (sin(t * pi * 2 + pi / 2) + 1) / 2)!, + Color.lerp(const Color(0xFFF8F9FA), + const Color(0xFFFFFFFF), (cos(t * pi * 2 + pi) + 1) / 2)!, ], stops: const [0.0, 0.3, 0.7, 1.0], ), diff --git a/lib/screens/mains/hunt/services/location_service.dart b/lib/screens/mains/hunt/services/location_service.dart index 5e56517..65039d3 100644 --- a/lib/screens/mains/hunt/services/location_service.dart +++ b/lib/screens/mains/hunt/services/location_service.dart @@ -33,6 +33,21 @@ class LocationService { } } + static Stream? getPositionStream() async* { + bool hasPermission = await checkLocationPermission(); + if (!hasPermission) { + yield* Stream.empty(); + return; + } + + const LocationSettings locationSettings = LocationSettings( + accuracy: LocationAccuracy.high, + distanceFilter: 1, + ); + + yield* Geolocator.getPositionStream(locationSettings: locationSettings); + } + static double calculateDistance( double lat1, double lon1, diff --git a/lib/screens/mains/hunt/widgets/hint_camera_widget.dart b/lib/screens/mains/hunt/widgets/hint_camera_widget.dart index dc93c0f..651ed02 100644 --- a/lib/screens/mains/hunt/widgets/hint_camera_widget.dart +++ b/lib/screens/mains/hunt/widgets/hint_camera_widget.dart @@ -4,13 +4,13 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:geolocator/geolocator.dart'; +import 'package:latlong2/latlong.dart'; import 'package:lba/gen/assets.gen.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:flutter_compass/flutter_compass.dart'; import 'package:lba/res/colors.dart'; import '../services/location_service.dart'; import 'package:flutter_map/flutter_map.dart'; -import 'package:latlong2/latlong.dart'; class HintCameraWidget extends StatefulWidget { final double targetLatitude; @@ -40,7 +40,7 @@ class _HintCameraWidgetState extends State with TickerProviderStateMixin { MobileScannerController? _controller; StreamSubscription? _compassSubscription; - Timer? _locationTimer; + StreamSubscription? _locationSubscription; Timer? _countdownTimer; final MapController _miniMapController = MapController(); @@ -51,7 +51,7 @@ class _HintCameraWidgetState extends State Position? _currentPosition; bool _isNearTarget = false; bool _isMapVisible = false; - + Duration _currentRemainingTime = Duration.zero; late AnimationController _rotationController; @@ -70,23 +70,24 @@ class _HintCameraWidgetState extends State _startLocationUpdates(); _initializeTimer(); } - + void _initializeTimer() { if (widget.remainingTime != null) { - _currentRemainingTime = widget.remainingTime!.inSeconds == 0 + _currentRemainingTime = widget.remainingTime!.inSeconds == 0 ? const Duration(hours: 12) : widget.remainingTime!; _startCountdownTimer(); } } - + void _startCountdownTimer() { _countdownTimer?.cancel(); _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) { if (mounted) { setState(() { if (_currentRemainingTime.inSeconds > 0) { - _currentRemainingTime = Duration(seconds: _currentRemainingTime.inSeconds - 1); + _currentRemainingTime = + Duration(seconds: _currentRemainingTime.inSeconds - 1); } else { timer.cancel(); } @@ -111,7 +112,7 @@ class _HintCameraWidgetState extends State duration: const Duration(milliseconds: 3500), vsync: this, )..repeat(); - + _rotationAnimation = Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation(parent: _rotationController, curve: Curves.linear), ); @@ -130,12 +131,8 @@ class _HintCameraWidgetState extends State } void _startLocationUpdates() { - _locationTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) async { - if (!mounted) { - timer.cancel(); - return; - } - final position = await LocationService.getCurrentPosition(); + _locationSubscription = + LocationService.getPositionStream()?.listen((Position? position) { if (position != null && mounted) { final bearing = LocationService.getBearing( position.latitude, @@ -168,8 +165,10 @@ class _HintCameraWidgetState extends State void _updateMiniMapCamera() { if (_currentPosition != null) { - final userLocation = LatLng(_currentPosition!.latitude, _currentPosition!.longitude); - final targetLocation = LatLng(widget.targetLatitude, widget.targetLongitude); + final userLocation = + LatLng(_currentPosition!.latitude, _currentPosition!.longitude); + final targetLocation = + LatLng(widget.targetLatitude, widget.targetLongitude); _miniMapController.fitCamera( CameraFit.bounds( bounds: LatLngBounds(userLocation, targetLocation), @@ -189,7 +188,7 @@ class _HintCameraWidgetState extends State double _getDistanceFontSize(double distance) { if (distance >= 10000) { - return 20; + return 20; } else if (distance >= 1000) { return 26; } else { @@ -211,7 +210,7 @@ class _HintCameraWidgetState extends State @override void dispose() { _compassSubscription?.cancel(); - _locationTimer?.cancel(); + _locationSubscription?.cancel(); _countdownTimer?.cancel(); _controller?.dispose(); _rotationController.dispose(); @@ -222,8 +221,10 @@ class _HintCameraWidgetState extends State Widget _buildMiniMap() { if (_currentPosition == null) return const SizedBox.shrink(); - final userLocation = LatLng(_currentPosition!.latitude, _currentPosition!.longitude); - final targetLocation = LatLng(widget.targetLatitude, widget.targetLongitude); + final userLocation = + LatLng(_currentPosition!.latitude, _currentPosition!.longitude); + final targetLocation = + LatLng(widget.targetLatitude, widget.targetLongitude); return AnimatedPositioned( duration: const Duration(milliseconds: 500), curve: Curves.easeInOutCubic, @@ -248,11 +249,13 @@ class _HintCameraWidgetState extends State options: MapOptions( initialCenter: userLocation, initialZoom: 15, - interactionOptions: const InteractionOptions(flags: InteractiveFlag.none), + interactionOptions: + const InteractionOptions(flags: InteractiveFlag.none), ), children: [ TileLayer( - urlTemplate: 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png', + urlTemplate: + 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png', subdomains: const ['a', 'b', 'c', 'd'], ), MarkerLayer( @@ -261,13 +264,13 @@ class _HintCameraWidgetState extends State point: userLocation, width: 40, height: 40, - child: PulsingMarker(isUser: true), + child: const PulsingMarker(isUser: true), ), Marker( point: targetLocation, width: 40, height: 40, - child: PulsingMarker(isUser: false), + child: const PulsingMarker(isUser: false), ), ], ), @@ -330,13 +333,16 @@ class _HintCameraWidgetState extends State ), child: IconButton( onPressed: widget.onClose, - icon: SvgPicture.asset(Assets.icons.back.path, color: Colors.white,), + icon: SvgPicture.asset(Assets.icons.back.path, + colorFilter: + const ColorFilter.mode(Colors.white, BlendMode.srcIn)), padding: EdgeInsets.zero, constraints: const BoxConstraints(), ), ), title: Container( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + padding: + const EdgeInsets.symmetric(horizontal: 20, vertical: 10), decoration: BoxDecoration( gradient: LinearGradient( colors: [ @@ -367,12 +373,11 @@ class _HintCameraWidgetState extends State child: const Text( 'AR Hint', style: TextStyle( - color: Colors.white, - fontSize: 15, - fontWeight: FontWeight.w700, - letterSpacing: 1.2, - fontFamily: 'monospace' - ), + color: Colors.white, + fontSize: 15, + fontWeight: FontWeight.w700, + letterSpacing: 1.2, + fontFamily: 'monospace'), ), ), centerTitle: true, @@ -400,9 +405,10 @@ class _HintCameraWidgetState extends State ], ), child: IconButton( - onPressed: () { - }, - icon: SvgPicture.asset(Assets.icons.infoCircle.path, color: Colors.white,), + onPressed: () {}, + icon: SvgPicture.asset(Assets.icons.infoCircle.path, + colorFilter: const ColorFilter.mode( + Colors.white, BlendMode.srcIn)), ), ), ], @@ -437,17 +443,19 @@ class _HintCameraWidgetState extends State Widget _buildTimerWidget() { if (widget.remainingTime == null) return const SizedBox.shrink(); - - final Duration timeToShow = _currentRemainingTime.inSeconds <= 0 - ? (widget.remainingTime!.inSeconds == 0 ? const Duration(hours: 12) : Duration.zero) + + final Duration timeToShow = _currentRemainingTime.inSeconds <= 0 + ? (widget.remainingTime!.inSeconds == 0 + ? const Duration(hours: 12) + : Duration.zero) : _currentRemainingTime; - + final hours = timeToShow.inHours; final minutes = timeToShow.inMinutes % 60; final seconds = timeToShow.inSeconds % 60; - final isUrgent = timeToShow.inMinutes < 5 && timeToShow.inHours == 0; - final isExpired = _currentRemainingTime.inSeconds <= 0 && widget.remainingTime!.inSeconds > 0; - + final isExpired = + _currentRemainingTime.inSeconds <= 0 && widget.remainingTime!.inSeconds > 0; + return Positioned( top: kToolbarHeight + 60, right: 16, @@ -469,15 +477,13 @@ class _HintCameraWidgetState extends State mainAxisSize: MainAxisSize.min, children: [ Text( - isExpired - ? 'Expired' - : hours > 0 - ? '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}' - : '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}', + isExpired + ? 'Expired' + : hours > 0 + ? '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}' + : '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}', style: TextStyle( - color: isExpired - ? const Color(0xFFFF3B30) - : Colors.white, + color: isExpired ? const Color(0xFFFF3B30) : Colors.white, fontSize: 15, fontWeight: FontWeight.w600, letterSpacing: 0.5, @@ -490,8 +496,6 @@ class _HintCameraWidgetState extends State ), ); } - - Widget _buildMapFAB() { return ClipRRect( @@ -575,7 +579,9 @@ class _HintCameraWidgetState extends State child: Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0), child: Icon( - isLeft ? Icons.arrow_back_ios_new_rounded : Icons.arrow_forward_ios_rounded, + isLeft + ? Icons.arrow_back_ios_new_rounded + : Icons.arrow_forward_ios_rounded, color: AppColors.primary.withOpacity(0.8), size: 48, shadows: [ @@ -636,14 +642,13 @@ class _HintCameraWidgetState extends State Text( _isNearTarget ? "TARGET ACQUIRED" : "SCANNING", style: TextStyle( - color: _isNearTarget - ? const Color(0xFF00E676) - : const Color(0xFF2196F3), - fontWeight: FontWeight.w600, - fontSize: 12, - letterSpacing: 1.5, - fontFamily: 'monospace' - ), + color: _isNearTarget + ? const Color(0xFF00E676) + : const Color(0xFF2196F3), + fontWeight: FontWeight.w600, + fontSize: 12, + letterSpacing: 1.5, + fontFamily: 'monospace'), ), const SizedBox(height: 12), Text( @@ -653,8 +658,7 @@ class _HintCameraWidgetState extends State fontWeight: FontWeight.w800, fontSize: _getDistanceFontSize(distance), letterSpacing: 0.5, - fontFamily: 'monospace' - ), + fontFamily: 'monospace'), ), ], ), @@ -701,9 +705,11 @@ class _PulsingMarkerState extends State Widget build(BuildContext context) { final color = widget.isUser ? Colors.blue.shade400 : Colors.yellow.shade700; return FadeTransition( - opacity: Tween(begin: 1.0, end: 0.3).animate(_animationController), + opacity: + Tween(begin: 1.0, end: 0.3).animate(_animationController), child: ScaleTransition( - scale: Tween(begin: 0.5, end: 1.0).animate(_animationController), + scale: + Tween(begin: 0.5, end: 1.0).animate(_animationController), child: Container( decoration: BoxDecoration( shape: BoxShape.circle, @@ -713,8 +719,11 @@ class _PulsingMarkerState extends State child: Padding( padding: const EdgeInsets.all(8.0), child: SvgPicture.asset( - widget.isUser ? Assets.icons.gps.path : Assets.icons.locationCross.path, - color: Colors.black, + widget.isUser + ? Assets.icons.gps.path + : Assets.icons.locationCross.path, + colorFilter: + const ColorFilter.mode(Colors.black, BlendMode.srcIn), width: 10, ), ), @@ -727,7 +736,7 @@ class _PulsingMarkerState extends State class HudPainter extends CustomPainter { final double animationValue; HudPainter({required this.animationValue}); - + @override void paint(Canvas canvas, Size size) { final paint = Paint() @@ -738,18 +747,31 @@ class HudPainter extends CustomPainter { const bracketSize = 30.0; const margin = 20.0; - canvas.drawLine(Offset(margin, margin), Offset(margin + bracketSize, margin), paint); - canvas.drawLine(Offset(margin, margin), Offset(margin, margin + bracketSize), paint); - canvas.drawLine(Offset(size.width - margin, margin), Offset(size.width - margin - bracketSize, margin), paint); - canvas.drawLine(Offset(size.width - margin, margin), Offset(size.width - margin, margin + bracketSize), paint); - canvas.drawLine(Offset(margin, size.height - margin), Offset(margin + bracketSize, size.height - margin), paint); - canvas.drawLine(Offset(margin, size.height - margin), Offset(margin, size.height - margin - bracketSize), paint); - canvas.drawLine(Offset(size.width - margin, size.height - margin), Offset(size.width - margin - bracketSize, size.height - margin), paint); - canvas.drawLine(Offset(size.width - margin, size.height - margin), Offset(size.width - margin, size.height - margin - bracketSize), paint); + canvas.drawLine( + Offset(margin, margin), Offset(margin + bracketSize, margin), paint); + canvas.drawLine( + Offset(margin, margin), Offset(margin, margin + bracketSize), paint); + canvas.drawLine(Offset(size.width - margin, margin), + Offset(size.width - margin - bracketSize, margin), paint); + canvas.drawLine(Offset(size.width - margin, margin), + Offset(size.width - margin, margin + bracketSize), paint); + canvas.drawLine(Offset(margin, size.height - margin), + Offset(margin + bracketSize, size.height - margin), paint); + canvas.drawLine(Offset(margin, size.height - margin), + Offset(margin, size.height - margin - bracketSize), paint); + canvas.drawLine( + Offset(size.width - margin, size.height - margin), + Offset(size.width - margin - bracketSize, size.height - margin), + paint); + canvas.drawLine( + Offset(size.width - margin, size.height - margin), + Offset(size.width - margin, size.height - margin - bracketSize), + paint); } @override - bool shouldRepaint(covariant HudPainter oldDelegate) => oldDelegate.animationValue != animationValue; + bool shouldRepaint(covariant HudPainter oldDelegate) => + oldDelegate.animationValue != animationValue; } class AdvancedTargetPainter extends CustomPainter { @@ -771,7 +793,8 @@ class AdvancedTargetPainter extends CustomPainter { void paint(Canvas canvas, Size size) { final center = Offset(size.width / 2, size.height / 2); final paint = Paint()..style = PaintingStyle.stroke; - final primaryColor = isNear ? const Color(0xFF00E676) : const Color(0xFF2196F3); + final primaryColor = + isNear ? const Color(0xFF00E676) : const Color(0xFF2196F3); final baseRadius = math.max(60.0, math.min(85.0, 100 - (distance / 2))); paint @@ -789,7 +812,7 @@ class AdvancedTargetPainter extends CustomPainter { rotationValue * 2 * math.pi, 2.5, false, paint); canvas.drawArc(Rect.fromCircle(center: center, radius: baseRadius), rotationValue * 2 * math.pi + 3, 2.5, false, paint); - + paint ..color = primaryColor.withOpacity(0.4) ..strokeWidth = 1.5; diff --git a/lib/screens/mains/hunt/widgets/hunt_card_widget.dart b/lib/screens/mains/hunt/widgets/hunt_card_widget.dart index 2ff3fa2..fc946ca 100644 --- a/lib/screens/mains/hunt/widgets/hunt_card_widget.dart +++ b/lib/screens/mains/hunt/widgets/hunt_card_widget.dart @@ -349,7 +349,7 @@ class _HuntCardWidgetState extends State Icons.stars_rounded, color: AppColors.isDarkMode ? AppColors.confirmButton - : const Color(0xFF2E7D32), + : const Color.fromARGB(255, 38, 121, 41), size: 16, ), const SizedBox(width: 4), @@ -358,7 +358,7 @@ class _HuntCardWidgetState extends State style: TextStyle( color: AppColors.isDarkMode ? AppColors.confirmButton - : const Color(0xFF2E7D32), + : const Color.fromARGB(255, 38, 121, 41), fontWeight: FontWeight.bold, fontSize: 14, ), @@ -443,15 +443,13 @@ class _HuntCardWidgetState extends State : const Color(0xFF1976D2).withOpacity(0.2), width: 1.5, ), - boxShadow: [ + boxShadow: AppColors.isDarkMode ? [ BoxShadow( - color: AppColors.isDarkMode - ? AppColors.primary.withOpacity(0.1) - : const Color(0xFF1976D2).withOpacity(0.06), + color: AppColors.primary.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, 4), ), - ], + ] : null, ), child: Column( children: [