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)!,
]
: [
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],
),

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(
double lat1,
double lon1,

View File

@ -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<HintCameraWidget>
with TickerProviderStateMixin {
MobileScannerController? _controller;
StreamSubscription<CompassEvent>? _compassSubscription;
Timer? _locationTimer;
StreamSubscription<Position>? _locationSubscription;
Timer? _countdownTimer;
final MapController _miniMapController = MapController();
@ -86,7 +86,8 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
if (mounted) {
setState(() {
if (_currentRemainingTime.inSeconds > 0) {
_currentRemainingTime = Duration(seconds: _currentRemainingTime.inSeconds - 1);
_currentRemainingTime =
Duration(seconds: _currentRemainingTime.inSeconds - 1);
} else {
timer.cancel();
}
@ -130,12 +131,8 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
}
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<HintCameraWidget>
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<HintCameraWidget>
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<HintCameraWidget>
@override
void dispose() {
_compassSubscription?.cancel();
_locationTimer?.cancel();
_locationSubscription?.cancel();
_countdownTimer?.cancel();
_controller?.dispose();
_rotationController.dispose();
@ -222,8 +221,10 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
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<HintCameraWidget>
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<HintCameraWidget>
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<HintCameraWidget>
),
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<HintCameraWidget>
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<HintCameraWidget>
],
),
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)),
),
),
],
@ -439,14 +445,16 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
if (widget.remainingTime == null) return const SizedBox.shrink();
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;
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,
@ -470,14 +478,12 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
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')}',
? '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,
@ -491,8 +497,6 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
);
}
Widget _buildMapFAB() {
return ClipRRect(
borderRadius: BorderRadius.circular(18),
@ -575,7 +579,9 @@ class _HintCameraWidgetState extends State<HintCameraWidget>
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<HintCameraWidget>
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<HintCameraWidget>
fontWeight: FontWeight.w800,
fontSize: _getDistanceFontSize(distance),
letterSpacing: 0.5,
fontFamily: 'monospace'
),
fontFamily: 'monospace'),
),
],
),
@ -701,9 +705,11 @@ class _PulsingMarkerState extends State<PulsingMarker>
Widget build(BuildContext context) {
final color = widget.isUser ? Colors.blue.shade400 : Colors.yellow.shade700;
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(
scale: Tween<double>(begin: 0.5, end: 1.0).animate(_animationController),
scale:
Tween<double>(begin: 0.5, end: 1.0).animate(_animationController),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
@ -713,8 +719,11 @@ class _PulsingMarkerState extends State<PulsingMarker>
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,
),
),
@ -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

View File

@ -349,7 +349,7 @@ class _HuntCardWidgetState extends State<HuntCardWidget>
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<HuntCardWidget>
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<HuntCardWidget>
: 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: [