import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; import 'package:geolocator/geolocator.dart'; import '../../core/location/location_service.dart'; import '../../core/utils/exif_helper.dart'; import '../../config/app_config.dart'; import 'image_preview_screen.dart'; import 'scan_mode_provider.dart'; import 'camera_drawer.dart'; class CameraScreen extends ConsumerStatefulWidget { const CameraScreen({super.key}); @override ConsumerState createState() => CameraScreenState(); } class CameraScreenState extends ConsumerState { bool _isCapturing = false; Position? _currentPosition; StreamSubscription? _locationSubscription; @override void initState() { super.initState(); _preFetchLocation(); } @override void dispose() { _locationSubscription?.cancel(); super.dispose(); } Future _preFetchLocation() async { try { final pos = await LocationService.determinePosition(); if (mounted) { setState(() { _currentPosition = pos; }); } // Live location updates _locationSubscription = LocationService.getPositionStream().listen( (Position freshPos) { if (mounted) { setState(() { _currentPosition = freshPos; }); } }, ); } catch (_) {} } Future _takePicture() async { if (_isCapturing) return; final ImagePicker picker = ImagePicker(); try { final XFile? image = await picker.pickImage( source: ImageSource.camera, maxWidth: 2048, maxHeight: 2048, imageQuality: 85, ); if (image != null) { setState(() { _isCapturing = true; }); // Use pre-fetched location if available, otherwise fetch quick position Position? position = _currentPosition; if (position == null) { try { position = await LocationService.determinePosition().timeout(const Duration(seconds: 2)); } catch (_) {} } setState(() { _isCapturing = false; }); navigateToPreview(image.path, latitude: position?.latitude, longitude: position?.longitude); } } catch (e) { debugPrint('Error taking picture: $e'); } } Future _pickFromGallery() async { if (_isCapturing) return; final ImagePicker picker = ImagePicker(); try { final XFile? image = await picker.pickImage( source: ImageSource.gallery, maxWidth: 2048, maxHeight: 2048, imageQuality: 85, ); if (image != null) { setState(() { _isCapturing = true; }); Position? position = _currentPosition; try { // Attempt to extract GPS from EXIF tags final exifGps = await ExifHelper.extractGps(image.path); if (exifGps != null) { position = Position( latitude: exifGps['latitude']!, longitude: exifGps['longitude']!, timestamp: DateTime.now(), accuracy: 0.0, altitude: 0.0, heading: 0.0, speed: 0.0, speedAccuracy: 0.0, altitudeAccuracy: 0.0, headingAccuracy: 0.0, ); } else if (position == null) { position = await LocationService.determinePosition().timeout(const Duration(seconds: 2)); } } catch (_) {} setState(() { _isCapturing = false; }); navigateToPreview(image.path, latitude: position?.latitude, longitude: position?.longitude); } } catch (e) { debugPrint('Error picking image from gallery: $e'); } } void navigateToPreview(String imagePath, {double? latitude, double? longitude}) { Navigator.push( context, MaterialPageRoute( builder: (context) => ImagePreviewScreen( imagePath: imagePath, latitude: latitude, longitude: longitude, ), ), ); } @override Widget build(BuildContext context) { final activeMode = ref.watch(scanModeProvider); return Scaffold( backgroundColor: AppConfig.backgroundColor, appBar: AppBar( title: Text(activeMode == 'DO' ? 'Pemindai Dokumen OCR' : 'Pemindai Produk PFM'), ), drawer: const CameraDrawer(), body: SafeArea( child: Padding( padding: AppConfig.screenPadding, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Upper Illustration Card Expanded( child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(AppConfig.borderRadiusXL), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 4), ) ], ), child: Padding( padding: AppConfig.paddingXL, child: SingleChildScrollView( child: activeMode == 'DO' ? Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 100, height: 100, decoration: BoxDecoration( color: AppConfig.primaryColor.withOpacity(0.1), shape: BoxShape.circle, ), child: const Icon( Icons.document_scanner, size: 48, color: AppConfig.primaryColor, ), ), const SizedBox(height: AppConfig.spaceXL), Text( 'Pemindai DO', style: AppConfig.h3, ), const SizedBox(height: AppConfig.spaceMedium), Text( 'Ambil foto lembar Delivery Order (DO) Prima Fresh Mart untuk mengekstrak data PO, SO, DO, dan item barang secara otomatis.', style: AppConfig.bodyMedium.copyWith(color: AppConfig.textSecondary), textAlign: TextAlign.center, ), const SizedBox(height: AppConfig.spaceXL), _buildGuidelineRow(Icons.lightbulb_outline, 'Pastikan pencahayaan cukup dan terang.'), const SizedBox(height: AppConfig.spaceSmall), _buildGuidelineRow(Icons.crop_free, 'Posisikan seluruh halaman dokumen di dalam foto.'), const SizedBox(height: AppConfig.spaceSmall), _buildGuidelineRow(Icons.camera_enhance_outlined, 'Pegang HP dengan stabil agar tidak buram.'), ], ) : Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 100, height: 100, decoration: BoxDecoration( color: AppConfig.primaryColor.withOpacity(0.1), shape: BoxShape.circle, ), child: const Icon( Icons.inventory_2_outlined, size: 48, color: AppConfig.primaryColor, ), ), const SizedBox(height: AppConfig.spaceXL), Text( 'Pemindai Produk', style: AppConfig.h3, ), const SizedBox(height: AppConfig.spaceMedium), Text( 'Ambil foto kemasan produk Prima Fresh Mart untuk mengidentifikasi SKU, nama item, dan mendeteksi tanggal kadaluwarsa secara otomatis.', style: AppConfig.bodyMedium.copyWith(color: AppConfig.textSecondary), textAlign: TextAlign.center, ), const SizedBox(height: AppConfig.spaceXL), _buildGuidelineRow(Icons.lightbulb_outline, 'Pastikan kode SKU atau teks kadaluwarsa terlihat jelas.'), const SizedBox(height: AppConfig.spaceSmall), _buildGuidelineRow(Icons.center_focus_strong, 'Posisikan produk di tengah kamera.'), const SizedBox(height: AppConfig.spaceSmall), _buildGuidelineRow(Icons.flash_on, 'Gunakan pencahayaan yang cukup agar tidak buram.'), ], ), ), ), ), ), const SizedBox(height: AppConfig.spaceXL), // Action Buttons ElevatedButton.icon( onPressed: _isCapturing ? null : _takePicture, icon: const Icon(Icons.camera_alt), label: const Text('Ambil Foto Kamera'), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 18), textStyle: AppConfig.bodyLarge.copyWith(fontWeight: FontWeight.bold), ), ), const SizedBox(height: AppConfig.spaceMedium), OutlinedButton.icon( onPressed: _isCapturing ? null : _pickFromGallery, icon: const Icon(Icons.photo_library), label: const Text('Pilih Dari Galeri'), style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 18), side: const BorderSide(color: AppConfig.primaryColor), foregroundColor: AppConfig.primaryColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppConfig.borderRadiusMedium), ), textStyle: AppConfig.bodyLarge.copyWith(fontWeight: FontWeight.bold), ), ), const SizedBox(height: AppConfig.spaceMedium), TextButton.icon( onPressed: _isCapturing ? null : () => context.push('/documents'), icon: const Icon(Icons.history_edu), label: const Text('Riwayat Dokumen'), style: TextButton.styleFrom( foregroundColor: AppConfig.textSecondary, ), ), ], ), ), ), ); } Widget _buildGuidelineRow(IconData icon, String text) { return Row( children: [ Icon(icon, size: 18, color: AppConfig.secondaryColor), const SizedBox(width: AppConfig.spaceSmall), Expanded( child: Text( text, style: AppConfig.bodySmall.copyWith(color: AppConfig.textSecondary), ), ), ], ); } }