update hunt light mode

This commit is contained in:
mohamadmahdi jebeli 2025-09-17 13:42:56 +03:30
parent 895ea862b0
commit 3f7ad2a3df
4 changed files with 130 additions and 94 deletions

View File

@ -151,14 +151,14 @@ class _HuntContentState extends State<_HuntContent> with TickerProviderStateMixi
const Color(0xFF0F172A), (cos(t * pi * 2 + pi) + 1) / 2)!, const Color(0xFF0F172A), (cos(t * pi * 2 + pi) + 1) / 2)!,
] ]
: [ : [
Color.lerp(const Color(0xFF84CEEB), Color.lerp(const Color(0xFFFFFFFF),
const Color(0xFF5680E9), (sin(t * pi * 2) + 1) / 2)!, const Color(0xFFF8F9FA), (sin(t * pi * 2) + 1) / 2)!,
Color.lerp(const Color(0xFF5680E9), Color.lerp(const Color(0xFFF8F9FA),
const Color(0xFF8860D0), (cos(t * pi * 2 + pi / 3) + 1) / 2)!, const Color(0xFFE9ECEF), (cos(t * pi * 2 + pi / 3) + 1) / 2)!,
Color.lerp(const Color(0xFF8860D0), Color.lerp(const Color(0xFFE9ECEF),
const Color(0xFF5AB9EA), (sin(t * pi * 2 + pi / 2) + 1) / 2)!, const Color(0xFFF8F9FA), (sin(t * pi * 2 + pi / 2) + 1) / 2)!,
Color.lerp(const Color(0xFF5AB9EA), Color.lerp(const Color(0xFFF8F9FA),
const Color(0xFF84CEEB), (cos(t * pi * 2 + pi) + 1) / 2)!, const Color(0xFFFFFFFF), (cos(t * pi * 2 + pi) + 1) / 2)!,
], ],
stops: const [0.0, 0.3, 0.7, 1.0], stops: const [0.0, 0.3, 0.7, 1.0],
), ),

View File

@ -33,6 +33,21 @@ class LocationService {
} }
} }
static Stream<Position>? 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( static double calculateDistance(
double lat1, double lat1,
double lon1, double lon1,

View File

@ -4,13 +4,13 @@ import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:geolocator/geolocator.dart'; import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart';
import 'package:lba/gen/assets.gen.dart'; import 'package:lba/gen/assets.gen.dart';
import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:flutter_compass/flutter_compass.dart'; import 'package:flutter_compass/flutter_compass.dart';
import 'package:lba/res/colors.dart'; import 'package:lba/res/colors.dart';
import '../services/location_service.dart'; import '../services/location_service.dart';
import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
class HintCameraWidget extends StatefulWidget { class HintCameraWidget extends StatefulWidget {
final double targetLatitude; final double targetLatitude;
@ -40,7 +40,7 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
with TickerProviderStateMixin { with TickerProviderStateMixin {
MobileScannerController? _controller; MobileScannerController? _controller;
StreamSubscription<CompassEvent>? _compassSubscription; StreamSubscription<CompassEvent>? _compassSubscription;
Timer? _locationTimer; StreamSubscription<Position>? _locationSubscription;
Timer? _countdownTimer; Timer? _countdownTimer;
final MapController _miniMapController = MapController(); final MapController _miniMapController = MapController();
@ -86,7 +86,8 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
if (mounted) { if (mounted) {
setState(() { setState(() {
if (_currentRemainingTime.inSeconds > 0) { if (_currentRemainingTime.inSeconds > 0) {
_currentRemainingTime = Duration(seconds: _currentRemainingTime.inSeconds - 1); _currentRemainingTime =
Duration(seconds: _currentRemainingTime.inSeconds - 1);
} else { } else {
timer.cancel(); timer.cancel();
} }
@ -130,12 +131,8 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
} }
void _startLocationUpdates() { void _startLocationUpdates() {
_locationTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) async { _locationSubscription =
if (!mounted) { LocationService.getPositionStream()?.listen((Position? position) {
timer.cancel();
return;
}
final position = await LocationService.getCurrentPosition();
if (position != null && mounted) { if (position != null && mounted) {
final bearing = LocationService.getBearing( final bearing = LocationService.getBearing(
position.latitude, position.latitude,
@ -168,8 +165,10 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
void _updateMiniMapCamera() { void _updateMiniMapCamera() {
if (_currentPosition != null) { if (_currentPosition != null) {
final userLocation = LatLng(_currentPosition!.latitude, _currentPosition!.longitude); final userLocation =
final targetLocation = LatLng(widget.targetLatitude, widget.targetLongitude); LatLng(_currentPosition!.latitude, _currentPosition!.longitude);
final targetLocation =
LatLng(widget.targetLatitude, widget.targetLongitude);
_miniMapController.fitCamera( _miniMapController.fitCamera(
CameraFit.bounds( CameraFit.bounds(
bounds: LatLngBounds(userLocation, targetLocation), bounds: LatLngBounds(userLocation, targetLocation),
@ -211,7 +210,7 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
@override @override
void dispose() { void dispose() {
_compassSubscription?.cancel(); _compassSubscription?.cancel();
_locationTimer?.cancel(); _locationSubscription?.cancel();
_countdownTimer?.cancel(); _countdownTimer?.cancel();
_controller?.dispose(); _controller?.dispose();
_rotationController.dispose(); _rotationController.dispose();
@ -222,8 +221,10 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
Widget _buildMiniMap() { Widget _buildMiniMap() {
if (_currentPosition == null) return const SizedBox.shrink(); if (_currentPosition == null) return const SizedBox.shrink();
final userLocation = LatLng(_currentPosition!.latitude, _currentPosition!.longitude); final userLocation =
final targetLocation = LatLng(widget.targetLatitude, widget.targetLongitude); LatLng(_currentPosition!.latitude, _currentPosition!.longitude);
final targetLocation =
LatLng(widget.targetLatitude, widget.targetLongitude);
return AnimatedPositioned( return AnimatedPositioned(
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 500),
curve: Curves.easeInOutCubic, curve: Curves.easeInOutCubic,
@ -248,11 +249,13 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
options: MapOptions( options: MapOptions(
initialCenter: userLocation, initialCenter: userLocation,
initialZoom: 15, initialZoom: 15,
interactionOptions: const InteractionOptions(flags: InteractiveFlag.none), interactionOptions:
const InteractionOptions(flags: InteractiveFlag.none),
), ),
children: [ children: [
TileLayer( 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'], subdomains: const ['a', 'b', 'c', 'd'],
), ),
MarkerLayer( MarkerLayer(
@ -261,13 +264,13 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
point: userLocation, point: userLocation,
width: 40, width: 40,
height: 40, height: 40,
child: PulsingMarker(isUser: true), child: const PulsingMarker(isUser: true),
), ),
Marker( Marker(
point: targetLocation, point: targetLocation,
width: 40, width: 40,
height: 40, height: 40,
child: PulsingMarker(isUser: false), child: const PulsingMarker(isUser: false),
), ),
], ],
), ),
@ -330,13 +333,16 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
), ),
child: IconButton( child: IconButton(
onPressed: widget.onClose, 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, padding: EdgeInsets.zero,
constraints: const BoxConstraints(), constraints: const BoxConstraints(),
), ),
), ),
title: Container( title: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
colors: [ colors: [
@ -371,8 +377,7 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
fontSize: 15, fontSize: 15,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
letterSpacing: 1.2, letterSpacing: 1.2,
fontFamily: 'monospace' fontFamily: 'monospace'),
),
), ),
), ),
centerTitle: true, centerTitle: true,
@ -400,9 +405,10 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
], ],
), ),
child: IconButton( child: IconButton(
onPressed: () { onPressed: () {},
}, icon: SvgPicture.asset(Assets.icons.infoCircle.path,
icon: SvgPicture.asset(Assets.icons.infoCircle.path, color: Colors.white,), colorFilter: const ColorFilter.mode(
Colors.white, BlendMode.srcIn)),
), ),
), ),
], ],
@ -439,14 +445,16 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
if (widget.remainingTime == null) return const SizedBox.shrink(); if (widget.remainingTime == null) return const SizedBox.shrink();
final Duration timeToShow = _currentRemainingTime.inSeconds <= 0 final Duration timeToShow = _currentRemainingTime.inSeconds <= 0
? (widget.remainingTime!.inSeconds == 0 ? const Duration(hours: 12) : Duration.zero) ? (widget.remainingTime!.inSeconds == 0
? const Duration(hours: 12)
: Duration.zero)
: _currentRemainingTime; : _currentRemainingTime;
final hours = timeToShow.inHours; final hours = timeToShow.inHours;
final minutes = timeToShow.inMinutes % 60; final minutes = timeToShow.inMinutes % 60;
final seconds = timeToShow.inSeconds % 60; final seconds = timeToShow.inSeconds % 60;
final isUrgent = timeToShow.inMinutes < 5 && timeToShow.inHours == 0; final isExpired =
final isExpired = _currentRemainingTime.inSeconds <= 0 && widget.remainingTime!.inSeconds > 0; _currentRemainingTime.inSeconds <= 0 && widget.remainingTime!.inSeconds > 0;
return Positioned( return Positioned(
top: kToolbarHeight + 60, top: kToolbarHeight + 60,
@ -475,9 +483,7 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
? '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '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')}', : '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}',
style: TextStyle( style: TextStyle(
color: isExpired color: isExpired ? const Color(0xFFFF3B30) : Colors.white,
? const Color(0xFFFF3B30)
: Colors.white,
fontSize: 15, fontSize: 15,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
letterSpacing: 0.5, letterSpacing: 0.5,
@ -491,8 +497,6 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
); );
} }
Widget _buildMapFAB() { Widget _buildMapFAB() {
return ClipRRect( return ClipRRect(
borderRadius: BorderRadius.circular(18), borderRadius: BorderRadius.circular(18),
@ -575,7 +579,9 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0), padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Icon( 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), color: AppColors.primary.withOpacity(0.8),
size: 48, size: 48,
shadows: [ shadows: [
@ -642,8 +648,7 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontSize: 12, fontSize: 12,
letterSpacing: 1.5, letterSpacing: 1.5,
fontFamily: 'monospace' fontFamily: 'monospace'),
),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Text( Text(
@ -653,8 +658,7 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
fontWeight: FontWeight.w800, fontWeight: FontWeight.w800,
fontSize: _getDistanceFontSize(distance), fontSize: _getDistanceFontSize(distance),
letterSpacing: 0.5, letterSpacing: 0.5,
fontFamily: 'monospace' fontFamily: 'monospace'),
),
), ),
], ],
), ),
@ -701,9 +705,11 @@ class _PulsingMarkerState extends State<PulsingMarker>
Widget build(BuildContext context) { Widget build(BuildContext context) {
final color = widget.isUser ? Colors.blue.shade400 : Colors.yellow.shade700; final color = widget.isUser ? Colors.blue.shade400 : Colors.yellow.shade700;
return FadeTransition( return FadeTransition(
opacity: Tween<double>(begin: 1.0, end: 0.3).animate(_animationController), opacity:
Tween<double>(begin: 1.0, end: 0.3).animate(_animationController),
child: ScaleTransition( child: ScaleTransition(
scale: Tween<double>(begin: 0.5, end: 1.0).animate(_animationController), scale:
Tween<double>(begin: 0.5, end: 1.0).animate(_animationController),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
@ -713,8 +719,11 @@ class _PulsingMarkerState extends State<PulsingMarker>
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: SvgPicture.asset( child: SvgPicture.asset(
widget.isUser ? Assets.icons.gps.path : Assets.icons.locationCross.path, widget.isUser
color: Colors.black, ? Assets.icons.gps.path
: Assets.icons.locationCross.path,
colorFilter:
const ColorFilter.mode(Colors.black, BlendMode.srcIn),
width: 10, width: 10,
), ),
), ),
@ -738,18 +747,31 @@ class HudPainter extends CustomPainter {
const bracketSize = 30.0; const bracketSize = 30.0;
const margin = 20.0; const margin = 20.0;
canvas.drawLine(Offset(margin, margin), Offset(margin + bracketSize, margin), paint); canvas.drawLine(
canvas.drawLine(Offset(margin, margin), Offset(margin, margin + bracketSize), paint); Offset(margin, margin), Offset(margin + bracketSize, margin), paint);
canvas.drawLine(Offset(size.width - margin, margin), Offset(size.width - margin - bracketSize, margin), paint); canvas.drawLine(
canvas.drawLine(Offset(size.width - margin, margin), Offset(size.width - margin, margin + bracketSize), paint); Offset(margin, margin), Offset(margin, margin + bracketSize), paint);
canvas.drawLine(Offset(margin, size.height - margin), Offset(margin + bracketSize, size.height - margin), paint); canvas.drawLine(Offset(size.width - margin, margin),
canvas.drawLine(Offset(margin, size.height - margin), Offset(margin, size.height - margin - bracketSize), paint); Offset(size.width - margin - bracketSize, margin), 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, margin),
canvas.drawLine(Offset(size.width - margin, size.height - margin), Offset(size.width - margin, size.height - margin - bracketSize), paint); 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 @override
bool shouldRepaint(covariant HudPainter oldDelegate) => oldDelegate.animationValue != animationValue; bool shouldRepaint(covariant HudPainter oldDelegate) =>
oldDelegate.animationValue != animationValue;
} }
class AdvancedTargetPainter extends CustomPainter { class AdvancedTargetPainter extends CustomPainter {
@ -771,7 +793,8 @@ class AdvancedTargetPainter extends CustomPainter {
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2); final center = Offset(size.width / 2, size.height / 2);
final paint = Paint()..style = PaintingStyle.stroke; 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))); final baseRadius = math.max(60.0, math.min(85.0, 100 - (distance / 2)));
paint paint

View File

@ -349,7 +349,7 @@ class _HuntCardWidgetState extends State<HuntCardWidget>
Icons.stars_rounded, Icons.stars_rounded,
color: AppColors.isDarkMode color: AppColors.isDarkMode
? AppColors.confirmButton ? AppColors.confirmButton
: const Color(0xFF2E7D32), : const Color.fromARGB(255, 38, 121, 41),
size: 16, size: 16,
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
@ -358,7 +358,7 @@ class _HuntCardWidgetState extends State<HuntCardWidget>
style: TextStyle( style: TextStyle(
color: AppColors.isDarkMode color: AppColors.isDarkMode
? AppColors.confirmButton ? AppColors.confirmButton
: const Color(0xFF2E7D32), : const Color.fromARGB(255, 38, 121, 41),
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 14, fontSize: 14,
), ),
@ -443,15 +443,13 @@ class _HuntCardWidgetState extends State<HuntCardWidget>
: const Color(0xFF1976D2).withOpacity(0.2), : const Color(0xFF1976D2).withOpacity(0.2),
width: 1.5, width: 1.5,
), ),
boxShadow: [ boxShadow: AppColors.isDarkMode ? [
BoxShadow( BoxShadow(
color: AppColors.isDarkMode color: AppColors.primary.withOpacity(0.1),
? AppColors.primary.withOpacity(0.1)
: const Color(0xFF1976D2).withOpacity(0.06),
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
], ] : null,
), ),
child: Column( child: Column(
children: [ children: [