import 'dart:async'; import 'dart:math' as math; import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:flutter_compass/flutter_compass.dart'; import 'package:sensors_plus/sensors_plus.dart'; import '../models/angle_unit.dart'; import '../models/landmark.dart'; import '../services/landmark_database.dart'; import '../services/local_sqlite_db.dart'; import '../services/resection_calculator.dart'; import '../services/tactical_api_service.dart'; import 'tactical_crosshair.dart'; class CameraResectionView extends StatefulWidget { final List observations; final AngleUnit angleUnit; final Function(ResectionObservation) onObservationAdded; final VoidCallback onCalculatePressed; final VoidCallback onResetPressed; final VoidCallback? onPickFromMap; final VoidCallback onClose; final TacticalLandmark? initialSelectedLandmark; const CameraResectionView({ super.key, required this.observations, required this.angleUnit, required this.onObservationAdded, required this.onCalculatePressed, required this.onResetPressed, this.onPickFromMap, required this.onClose, this.initialSelectedLandmark, }); @override State createState() => _CameraResectionViewState(); } class _CameraResectionViewState extends State { CameraController? _cameraController; bool _isCameraReady = false; double _headingDeg = 0.0; bool _isLockedAnim = false; double _currentZoom = 1.0; double _minZoom = 1.0; double _maxZoom = 8.0; double _baseScale = 1.0; // ── Single Landmark 15m Mode State ───────────────────────────────────────── bool _isSingleLandmarkMode = false; int _singlePhase = 0; // 0: Aim 1, 1: Walk 15m, 2: Aim 2 double _firstAzimuth = 0.0; double _secondAzimuth = 0.0; double _walkedDistanceMeters = 0.0; int _stepCount = 0; dynamic _accelSubscription; DateTime _lastStepTime = DateTime.now(); // Search state final TextEditingController _searchController = TextEditingController(); List _searchResults = []; bool _isSearching = false; TacticalLandmark? _selectedLandmark; @override void initState() { super.initState(); _initCamera(); _initCompass(); _initDefaultLandmark(); } void _initDefaultLandmark() { if (widget.initialSelectedLandmark != null) { _selectedLandmark = widget.initialSelectedLandmark; _searchResults = [widget.initialSelectedLandmark!, ...LandmarkDatabase.allLandmarks.take(9)]; return; } final all = LandmarkDatabase.allLandmarks; if (all.isNotEmpty) { _selectedLandmark = all[0]; _searchResults = all.take(10).toList(); } } Future _initCamera() async { try { final cameras = await availableCameras(); if (cameras.isNotEmpty) { _cameraController = CameraController( cameras.first, ResolutionPreset.high, enableAudio: false, ); await _cameraController!.initialize(); _minZoom = await _cameraController!.getMinZoomLevel(); _maxZoom = math.min(10.0, await _cameraController!.getMaxZoomLevel()); _currentZoom = _minZoom; if (mounted) { setState(() => _isCameraReady = true); } } } catch (e) { debugPrint('Camera init exception: $e'); } } Future _setZoom(double zoom) async { final clamped = zoom.clamp(_minZoom, _maxZoom); if (clamped != _currentZoom) { setState(() => _currentZoom = clamped); try { await _cameraController?.setZoomLevel(clamped); } catch (_) {} } } bool _isFrameFrozen = false; void _initCompass() { FlutterCompass.events?.listen((event) { if (event.heading != null && mounted) { if (_isFrameFrozen) return; // Keep heading locked while frame is frozen final raw = (event.heading! + 360) % 360; final diff = ((raw - _headingDeg + 180) % 360) - 180; final smoothed = (_headingDeg + diff * 0.35 + 360) % 360; setState(() { _headingDeg = smoothed; }); } }); } Future _toggleFreezeFrame() async { if (_isFrameFrozen) { try { await _cameraController?.resumePreview(); } catch (_) {} if (mounted) { setState(() => _isFrameFrozen = false); } } else { try { await _cameraController?.pausePreview(); if (mounted) { setState(() => _isFrameFrozen = true); } } catch (e) { debugPrint('Freeze frame pause error: $e'); } } } @override void dispose() { _accelSubscription?.cancel(); _cameraController?.dispose(); _searchController.dispose(); super.dispose(); } Future _onSearchChanged(String query) async { final cleanQuery = query.trim(); if (cleanQuery.isEmpty) { setState(() { _searchResults = LandmarkDatabase.allLandmarks.take(10).toList(); _isSearching = false; }); return; } setState(() => _isSearching = true); List dbResults = []; try { dbResults = await LocalSqliteDb.searchPlaces(cleanQuery, limit: 30); } catch (_) {} final staticResults = LandmarkDatabase.search(cleanQuery); List onlineResults = []; if (dbResults.length < 5) { try { onlineResults = await TacticalApiService.searchOnlinePlaces(cleanQuery); } catch (_) {} } final combined = [...dbResults, ...onlineResults, ...staticResults]; final seen = {}; final unique = []; for (final r in combined) { if (seen.add('${r.name}_${r.lat}_${r.lng}')) { unique.add(r); } } if (mounted) { setState(() { _searchResults = unique; _isSearching = false; if (_searchResults.isNotEmpty && !_searchResults.any((l) => l.id == _selectedLandmark?.id)) { _selectedLandmark = _searchResults[0]; } }); } } void _startPedometer() { _stepCount = 0; _walkedDistanceMeters = 0.0; _accelSubscription?.cancel(); _accelSubscription = accelerometerEventStream().listen((event) { final mag = math.sqrt(event.x * event.x + event.y * event.y + event.z * event.z); final now = DateTime.now(); if (mag > 11.5 && now.difference(_lastStepTime).inMilliseconds > 300) { _lastStepTime = now; if (mounted) { setState(() { _stepCount++; _walkedDistanceMeters = math.min(15.0, _stepCount * 0.75); if (_walkedDistanceMeters >= 15.0 && _singlePhase == 1) { _singlePhase = 2; } }); } } }); } void _lockSingleLandmarkPhase() { if (_selectedLandmark == null) return; if (_singlePhase == 0) { _firstAzimuth = _headingDeg; _singlePhase = 1; _startPedometer(); setState(() => _isLockedAnim = true); Future.delayed(const Duration(milliseconds: 300), () { if (mounted) setState(() => _isLockedAnim = false); }); } else if (_singlePhase == 1) { _singlePhase = 2; _accelSubscription?.cancel(); setState(() {}); } else if (_singlePhase == 2) { _secondAzimuth = _headingDeg; _accelSubscription?.cancel(); final obs = ResectionObservation( landmark: _selectedLandmark!, observedAzimuthDeg: _firstAzimuth, trueAzimuthDeg: ResectionCalculator.getTrueAzimuth(_firstAzimuth), ); final obs2 = ResectionObservation( landmark: _selectedLandmark!, observedAzimuthDeg: _secondAzimuth, trueAzimuthDeg: ResectionCalculator.getTrueAzimuth(_secondAzimuth), ); widget.onObservationAdded(obs); widget.onObservationAdded(obs2); setState(() => _isLockedAnim = true); Future.delayed(const Duration(milliseconds: 400), () { if (mounted) { widget.onCalculatePressed(); } }); } } void _lockCurrentObservation() { if (_isSingleLandmarkMode) { _lockSingleLandmarkPhase(); return; } if (_selectedLandmark == null) return; final trueAzimuth = ResectionCalculator.getTrueAzimuth(_headingDeg); final obs = ResectionObservation( landmark: _selectedLandmark!, observedAzimuthDeg: _headingDeg, trueAzimuthDeg: trueAzimuth, ); setState(() => _isLockedAnim = true); Future.delayed(const Duration(milliseconds: 300), () { if (mounted) setState(() => _isLockedAnim = false); }); widget.onObservationAdded(obs); // Auto-advance to map calculation if we have 2 observations final nextObsCount = widget.observations.length + (widget.observations.any((o) => o.landmark.id == obs.landmark.id) ? 0 : 1); if (nextObsCount >= 2) { Future.delayed(const Duration(milliseconds: 400), () { if (mounted) { widget.onCalculatePressed(); } }); return; } // Reset search for second landmark _searchController.clear(); setState(() { _searchResults = LandmarkDatabase.allLandmarks.where((l) => l.id != _selectedLandmark!.id).take(10).toList(); if (_searchResults.isNotEmpty) { _selectedLandmark = _searchResults[0]; } }); } @override Widget build(BuildContext context) { final trueAzimuth = ResectionCalculator.getTrueAzimuth(_headingDeg); final obsCount = widget.observations.length; return Scaffold( backgroundColor: Colors.black, body: Stack( children: [ // ── 1. Camera Feed / Viewfinder Background with Pinch Zoom ─ if (_isCameraReady && _cameraController != null) GestureDetector( onScaleStart: (_) => _baseScale = _currentZoom, onScaleUpdate: (details) => _setZoom(_baseScale * details.scale), child: SizedBox.expand( child: FittedBox( fit: BoxFit.cover, child: SizedBox( width: _cameraController!.value.previewSize?.height ?? 1, height: _cameraController!.value.previewSize?.width ?? 1, child: CameraPreview(_cameraController!), ), ), ), ) else Container( decoration: const BoxDecoration( gradient: RadialGradient( center: Alignment.center, radius: 1.2, colors: [Color(0xFF0F172A), Color(0xFF020617)], ), ), child: const Center( child: Icon(Icons.camera_alt, size: 48, color: Color(0xFF38BDF8)), ), ), // ── Controls on Right Side (Freeze Frame & Zoom Slider) ─── if (_isCameraReady) Positioned( right: 14, top: 210, bottom: 230, child: Column( children: [ // Freeze Frame Button GestureDetector( onTap: _toggleFreezeFrame, child: Container( width: 38, height: 38, decoration: BoxDecoration( color: _isFrameFrozen ? const Color(0xFFEF4444) : const Color(0xFF090E17).withAlpha(200), shape: BoxShape.circle, border: Border.all( color: _isFrameFrozen ? const Color(0xFFFF7171) : const Color(0xFF00F0FF), width: 1.5, ), boxShadow: [ if (_isFrameFrozen) const BoxShadow(color: Color(0x99EF4444), blurRadius: 10), ], ), child: Icon( _isFrameFrozen ? Icons.pause : Icons.camera_alt_outlined, color: Colors.white, size: 18, ), ), ), const SizedBox(height: 8), // Zoom Slider Container Expanded( child: Container( width: 38, padding: const EdgeInsets.symmetric(vertical: 6), decoration: BoxDecoration( color: const Color(0xFF090E17).withAlpha(200), borderRadius: BorderRadius.circular(18), border: Border.all(color: Colors.white24), ), child: Column( children: [ const Text('8x', style: TextStyle(color: Colors.white54, fontSize: 8.5)), Expanded( child: RotatedBox( quarterTurns: 3, child: SliderTheme( data: SliderThemeData( trackHeight: 2.5, thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 6), activeTrackColor: const Color(0xFF00F0FF), inactiveTrackColor: Colors.white24, thumbColor: Colors.white, ), child: Slider( value: _currentZoom, min: _minZoom, max: _maxZoom, onChanged: (val) => _setZoom(val), ), ), ), ), const Text('1x', style: TextStyle(color: Colors.white54, fontSize: 8.5)), ], ), ), ), ], ), ), // ── Frozen Frame Indicator Banner ───────────────────────── if (_isFrameFrozen) Positioned( top: 155, left: 20, right: 20, child: Center( child: Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6), decoration: BoxDecoration( color: const Color(0xEEEF4444), borderRadius: BorderRadius.circular(10), boxShadow: const [BoxShadow(color: Color(0x66EF4444), blurRadius: 10)], ), child: const Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.ac_unit, color: Colors.white, size: 14), SizedBox(width: 6), Text( 'تم تجميد الصورة لمنع الاهتزاز — سدد واقفل الرصد', style: TextStyle(color: Colors.white, fontSize: 11, fontWeight: FontWeight.bold), ), ], ), ), ), ), // ── 2. Tactical Military Crosshair & Reticle ──────────────── TacticalCrosshair( headingDeg: _headingDeg, trueAzimuthDeg: trueAzimuth, angleUnit: widget.angleUnit, isAimLocked: _isLockedAnim, targetName: _selectedLandmark?.name, ), // ── 3. Top Header: Step Flow + Search Bar ──────────────────── Positioned( top: 44, left: 16, right: 16, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Top Action Row Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Step Indicator Badge Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: const Color(0xF00F172A), borderRadius: BorderRadius.circular(20), border: Border.all( color: (_isSingleLandmarkMode ? _singlePhase >= 2 : obsCount >= 2) ? const Color(0xFF22C55E) : const Color(0xFF0071E3), width: 1.5, ), ), child: Row( children: [ Icon( (_isSingleLandmarkMode ? _singlePhase >= 2 : obsCount >= 2) ? Icons.check_circle : Icons.gps_fixed, size: 15, color: (_isSingleLandmarkMode ? _singlePhase >= 2 : obsCount >= 2) ? const Color(0xFF4ADE80) : const Color(0xFF38BDF8), ), const SizedBox(width: 6), Text( _isSingleLandmarkMode ? (_singlePhase == 0 ? '1. سدد على المعلم واقفل الزاوية الأولى' : (_singlePhase == 1 ? '2. تحرك 15 متراً عمودياً على خط النظر' : '3. سدد مرة أخرى واقفل الزاوية الثانية')) : (obsCount == 0 ? '1. ابحث وسدد على المعلم الأول' : (obsCount == 1 ? '2. ابحث وسدد على المعلم الثاني' : 'تم رصد $obsCount معالم • جاري الحساب')), style: TextStyle( color: (_isSingleLandmarkMode ? _singlePhase >= 2 : obsCount >= 2) ? const Color(0xFF4ADE80) : Colors.white, fontSize: 11, fontWeight: FontWeight.bold, ), ), ], ), ), // Close Button IconButton.filled( onPressed: widget.onClose, icon: const Icon(Icons.close, size: 18), style: IconButton.styleFrom( backgroundColor: const Color(0xCC0F172A), foregroundColor: Colors.white, ), ), ], ), const SizedBox(height: 8), // Mode Selector Toggle (رصد متعدد vs معلم واحد + 15م بالحساسات) Container( padding: const EdgeInsets.all(3), decoration: BoxDecoration( color: const Color(0xCC0F172A), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white12), ), child: Row( children: [ Expanded( child: GestureDetector( onTap: () { setState(() { _isSingleLandmarkMode = false; _singlePhase = 0; _accelSubscription?.cancel(); }); }, child: Container( padding: const EdgeInsets.symmetric(vertical: 6), decoration: BoxDecoration( color: !_isSingleLandmarkMode ? const Color(0xFF0071E3) : Colors.transparent, borderRadius: BorderRadius.circular(9), ), alignment: Alignment.center, child: Text( '🎯 تقاطع متعدد (2-3 معالم)', style: TextStyle( color: !_isSingleLandmarkMode ? Colors.white : const Color(0xFF94A3B8), fontSize: 11, fontWeight: FontWeight.bold, ), ), ), ), ), Expanded( child: GestureDetector( onTap: () { setState(() { _isSingleLandmarkMode = true; _singlePhase = 0; }); }, child: Container( padding: const EdgeInsets.symmetric(vertical: 6), decoration: BoxDecoration( color: _isSingleLandmarkMode ? const Color(0xFF10B981) : Colors.transparent, borderRadius: BorderRadius.circular(9), ), alignment: Alignment.center, child: Text( '📏 معلم واحد (خط أساس 15م)', style: TextStyle( color: _isSingleLandmarkMode ? Colors.white : const Color(0xFF94A3B8), fontSize: 11, fontWeight: FontWeight.bold, ), ), ), ), ), ], ), ), const SizedBox(height: 8), // Interactive Map Picker Trigger Button if (widget.onPickFromMap != null) Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Material( color: Colors.transparent, child: InkWell( onTap: widget.onPickFromMap, borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFF7C3AED), Color(0xFF4F46E5)], ), borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0xFFA78BFA), width: 1.2), boxShadow: const [ BoxShadow( color: Color(0x667C3AED), blurRadius: 8, offset: Offset(0, 2), ), ], ), child: const Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.touch_app, color: Colors.white, size: 16), SizedBox(width: 8), Text( '📍 تأشير واختيار المعلم مباشرة من الخريطة', style: TextStyle( color: Colors.white, fontSize: 11.5, fontWeight: FontWeight.bold, ), ), ], ), ), ), ), ), // Unified Place / Mosque / Landmark Search Field Container( decoration: BoxDecoration( color: const Color(0xF50F172A), borderRadius: BorderRadius.circular(14), border: Border.all(color: Colors.white24), ), child: TextField( controller: _searchController, onChanged: _onSearchChanged, style: const TextStyle(color: Colors.white, fontSize: 13), decoration: InputDecoration( hintText: 'ابحث عن معلم، مئذنة، مدرسة، قمة جبل...', hintStyle: const TextStyle(color: Color(0xFF64748B), fontSize: 12), prefixIcon: const Icon(Icons.search, color: Color(0xFF38BDF8), size: 18), suffixIcon: _searchController.text.isNotEmpty ? IconButton( icon: const Icon(Icons.clear, color: Color(0xFF94A3B8), size: 16), onPressed: () { _searchController.clear(); _onSearchChanged(''); }, ) : null, contentPadding: const EdgeInsets.symmetric(vertical: 10), border: InputBorder.none, ), ), ), if (_isSearching) ...[ const SizedBox(height: 4), const LinearProgressIndicator(minHeight: 2, color: Color(0xFF0071E3), backgroundColor: Colors.transparent), ], // Search Results Dropdown List (if search has text or active) if (_searchController.text.isNotEmpty && _searchResults.isNotEmpty) Container( margin: const EdgeInsets.only(top: 6), constraints: const BoxConstraints(maxHeight: 180), decoration: BoxDecoration( color: const Color(0xF80F172A), borderRadius: BorderRadius.circular(14), border: Border.all(color: Colors.white12), boxShadow: const [BoxShadow(color: Colors.black87, blurRadius: 20)], ), child: ListView.separated( padding: const EdgeInsets.symmetric(vertical: 4), shrinkWrap: true, itemCount: _searchResults.length, separatorBuilder: (_, __) => const Divider(color: Colors.white10, height: 1), itemBuilder: (context, idx) { final lm = _searchResults[idx]; final isSelected = lm.id == _selectedLandmark?.id; return ListTile( dense: true, visualDensity: VisualDensity.compact, leading: Icon( lm.typeIcon, color: isSelected ? const Color(0xFF38BDF8) : const Color(0xFF94A3B8), size: 18, ), title: Text( lm.name, style: TextStyle( color: isSelected ? const Color(0xFF38BDF8) : Colors.white, fontSize: 12, fontWeight: FontWeight.bold, ), ), subtitle: Text( '${lm.region} • ${lm.elevationM}م', style: const TextStyle(color: Color(0xFF64748B), fontSize: 10), ), trailing: isSelected ? const Icon(Icons.check, color: Color(0xFF38BDF8), size: 16) : null, onTap: () { setState(() => _selectedLandmark = lm); _searchController.text = lm.name; }, ); }, ), ), ], ), ), // ── 4. Bottom HUD: Active Target Details & Lock Action ─────── Positioned( bottom: 24, left: 16, right: 16, child: Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: const Color(0xF50F172A), borderRadius: BorderRadius.circular(20), border: Border.all( color: _isSingleLandmarkMode ? const Color(0xFF10B981) : const Color(0xFF0071E3), width: 1.5, ), boxShadow: const [BoxShadow(color: Colors.black87, blurRadius: 25)], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Live Pedometer & Sensor 15m Guidance Card (When in Single Landmark Phase 1) if (_isSingleLandmarkMode && _singlePhase == 1) Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(12), decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0x3310B981), Color(0x22059669)], ), borderRadius: BorderRadius.circular(14), border: Border.all(color: const Color(0xFF10B981), width: 1.2), ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ const Icon(Icons.directions_walk, color: Color(0xFF34D399), size: 20), const SizedBox(width: 8), Text( 'تحرك 15 متراً عمودياً على خط النظر ($_stepCount خطوة)', style: const TextStyle( color: Colors.white, fontSize: 11.5, fontWeight: FontWeight.bold, ), ), ], ), Text( '${_walkedDistanceMeters.toStringAsFixed(1)}م / 15.0م', style: const TextStyle( color: Color(0xFF34D399), fontSize: 13, fontWeight: FontWeight.w900, ), ), ], ), const SizedBox(height: 8), ClipRRect( borderRadius: BorderRadius.circular(6), child: LinearProgressIndicator( value: (_walkedDistanceMeters / 15.0).clamp(0.0, 1.0), minHeight: 8, backgroundColor: Colors.white12, valueColor: const AlwaysStoppedAnimation(Color(0xFF10B981)), ), ), ], ), ), // Targeted Landmark Card if (_selectedLandmark != null) Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: const Color(0x661E293B), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white12), ), child: Row( children: [ Icon(_selectedLandmark!.typeIcon, color: const Color(0xFF38BDF8), size: 22), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _selectedLandmark!.name, style: const TextStyle( color: Colors.white, fontSize: 13, fontWeight: FontWeight.bold, ), overflow: TextOverflow.ellipsis, ), Text( '${_selectedLandmark!.region} • إحداثيات: ${_selectedLandmark!.lat.toStringAsFixed(4)}, ${_selectedLandmark!.lng.toStringAsFixed(4)}', style: const TextStyle(color: Color(0xFF94A3B8), fontSize: 10), ), ], ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: const Color(0x330071E3), borderRadius: BorderRadius.circular(8), ), child: Text( AngleFormatter.format(trueAzimuth, widget.angleUnit, includeLabel: false), style: const TextStyle( color: Color(0xFF38BDF8), fontSize: 12, fontWeight: FontWeight.w900, ), ), ), ], ), ), // Main Lock / Action Button Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: _selectedLandmark != null ? _lockCurrentObservation : null, icon: Icon( _isSingleLandmarkMode ? (_singlePhase == 1 ? Icons.check : (_singlePhase == 2 ? Icons.my_location : Icons.lock_outline)) : (obsCount >= 1 ? Icons.my_location : Icons.lock_outline), size: 18, ), label: Text( _isSingleLandmarkMode ? (_singlePhase == 0 ? 'تثبيت الرصد الأول وبدء خط الأساس 15م' : (_singlePhase == 1 ? 'وصلت 15 متراً (انتقل لتسديد الرصد الثاني)' : 'تثبيت الرصد الثاني (حساب المسافة والموقع فوراً)')) : (obsCount == 0 ? 'تثبيت رصد المعلم الأول' : 'تثبيت رصد المعلم الثاني (استخراج الموقع فوراً)'), style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w900), ), style: ElevatedButton.styleFrom( backgroundColor: _isSingleLandmarkMode ? (_singlePhase == 1 ? const Color(0xFFF59E0B) : const Color(0xFF10B981)) : (obsCount >= 1 ? const Color(0xFF22C55E) : const Color(0xFF0071E3)), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 13), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), elevation: 4, ), ), ), if (obsCount > 0 || (_isSingleLandmarkMode && _singlePhase > 0)) ...[ const SizedBox(width: 8), IconButton.filledTonal( onPressed: () { if (_isSingleLandmarkMode) { setState(() { _singlePhase = 0; _stepCount = 0; _walkedDistanceMeters = 0.0; _accelSubscription?.cancel(); }); } else { widget.onResetPressed(); } }, icon: const Icon(Icons.refresh, size: 18), style: IconButton.styleFrom( backgroundColor: const Color(0x33EF4444), foregroundColor: const Color(0xFFF87171), ), ), ], ], ), ], ), ), ), ], ), ); } }