import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../domain/entities/quality_status.dart'; import '../providers/display_config_provider.dart'; import 'fingerprint_guide_painter.dart'; class CameraPreviewWidget extends ConsumerWidget { final CameraController? controller; final QualityStatus? qualityStatus; final int consecutivePasses; final bool canCapture; const CameraPreviewWidget({ super.key, required this.controller, this.qualityStatus, this.consecutivePasses = 0, this.canCapture = false, }); @override Widget build(BuildContext context, WidgetRef ref) { final config = ref.watch(displayConfigProvider); // If controller is null or not initialized, show Simulation/Placeholder final bool showCamera = controller != null && controller!.value.isInitialized; final qs = qualityStatus; final isFocusGood = qs?.isFocusGood ?? false; final isLightingGood = qs?.isLightingGood ?? false; final isPositionGood = qs?.isPositionGood ?? false; final overallGood = qs?.canCapture ?? false; return Stack( fit: StackFit.expand, children: [ if (showCamera) _buildCoveredCameraPreview(context, controller!) else Container( color: Colors.grey.shade900, child: const Center( child: Text( "Camera Unavailable\n(Simulation Mode)", textAlign: TextAlign.center, style: TextStyle(color: Colors.white54, fontSize: 16), ), ), ), // Fingerprint Guide (Painter) if (config.showGuide) CustomPaint( painter: FingerprintGuidePainter(isGoodQuality: overallGood), child: Container(), ), // Icon (Center Guide) if (config.showGuide) Center( child: Icon( Icons.fingerprint, color: overallGood ? Colors.greenAccent : Colors.white.withValues(alpha: 0.3), size: 100, ), ), // Quality Indicators (Top) if (config.showQualityIndicators) Positioned( top: 60, left: 0, right: 0, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildStatusIcon( Icons.center_focus_strong, "Focus", isFocusGood, ), _buildStatusIcon(Icons.wb_sunny, "Light", isLightingGood), _buildStatusIcon(Icons.crop_free, "Position", isPositionGood), ], ), ), // Status Text (Bottom) if (config.showStatusText) Positioned( bottom: 120, // Above FAB left: 20, right: 20, child: Column( children: [ if (overallGood && !canCapture) Text( "Hold Steady... $consecutivePasses/5", style: const TextStyle( color: Colors.yellowAccent, fontSize: 20, fontWeight: FontWeight.bold, shadows: [Shadow(blurRadius: 4, color: Colors.black)], ), ) else if (canCapture) const Text( "READY TO CAPTURE", style: TextStyle( color: Colors.greenAccent, fontSize: 20, fontWeight: FontWeight.bold, shadows: [Shadow(blurRadius: 4, color: Colors.black)], ), ) else const Text( "Adjust Finger", style: TextStyle( color: Colors.white, fontSize: 18, shadows: [Shadow(blurRadius: 4, color: Colors.black)], ), ), const SizedBox(height: 8), if (qs != null && config.showDebugInfo) Text( "Blur: ${qs.blurScore.toStringAsFixed(0)} | Bright: ${qs.brightness.toStringAsFixed(0)}", style: const TextStyle(color: Colors.grey, fontSize: 12), ), ], ), ), ], ); } Widget _buildCoveredCameraPreview( BuildContext context, CameraController controller, ) { final size = MediaQuery.of(context).size; double scale = 1.0; final double screenRatio = size.aspectRatio; final double cameraRatio = controller.value.aspectRatio; // Calculate effective camera ratio displayed (swapped if portrait) // We assume if screen is portrait, camera preview is also rendered in portrait mode (1/ratio) // by the CameraPreview widget. final double effectiveCameraRatio = (size.width < size.height) ? (1 / cameraRatio) : cameraRatio; if (screenRatio > effectiveCameraRatio) { // Screen is wider than camera preview scale = screenRatio / effectiveCameraRatio; } else { // Screen is taller than camera preview scale = effectiveCameraRatio / screenRatio; } return Transform.scale( scale: scale, alignment: Alignment.center, child: Center(child: CameraPreview(controller)), ); } Widget _buildStatusIcon(IconData icon, String label, bool isGood) { return Column( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: isGood ? Colors.green.withValues(alpha: 0.8) : Colors.red.withValues(alpha: 0.8), shape: BoxShape.circle, ), child: Icon(icon, color: Colors.white, size: 24), ), const SizedBox(height: 4), Text( label, style: const TextStyle( color: Colors.white, fontSize: 12, shadows: [Shadow(blurRadius: 2, color: Colors.black)], ), ), ], ); } }