// Versione: FINAL - FIX CONTATORE VISIBILE import 'package:flutter/material.dart'; import 'global_data.dart'; import 'comp_12.dart'; class Comp10Screen extends StatefulWidget { const Comp10Screen({super.key}); @override _Comp10ScreenState createState() => _Comp10ScreenState(); } class _Comp10ScreenState extends State { // Set per gestire selezioni multiple uniche Set _selectedSectors = {}; final double _canvasWidth = 300.0; final double _canvasHeight = 220.0; late TextEditingController _controllerDanni; late TextEditingController _controllerOsservazioni; final bool isB = GlobalData.latoCorrente == 'B'; late Map _hitboxes; @override void initState() { super.initState(); _selectedSectors = isB ? Set.from(GlobalData.puntiUrtoB_List) : Set.from(GlobalData.puntiUrtoA_List); _controllerDanni = TextEditingController(text: isB ? GlobalData.danni_visibili_B : GlobalData.danni_visibili_A); _controllerOsservazioni = TextEditingController(text: isB ? GlobalData.osservazioni_B : GlobalData.osservazioni_A); _hitboxes = isB ? _getHitboxesB() : _getHitboxesA(); // MOSTRA IL POPUP INFORMATIVO ANIMATO AL CARICAMENTO WidgetsBinding.instance.addPostFrameCallback((_) { _mostraInfoPopup(context); }); } // --- POPUP INFORMATIVO --- void _mostraInfoPopup(BuildContext context) { Color activeColor = isB ? Colors.amber.shade700 : Colors.blue.shade900; showGeneralDialog( context: context, barrierDismissible: false, barrierLabel: "Popup", barrierColor: Colors.black.withOpacity(0.5), transitionDuration: const Duration(milliseconds: 400), pageBuilder: (context, animation, secondaryAnimation) { return AlertDialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), title: Row( children: [ Icon(Icons.minor_crash, color: activeColor, size: 28), const SizedBox(width: 10), const Expanded(child: Text("Danni e Osservazioni", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18))), ], ), content: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text("In questa sezione indicherai i danni subiti dal Veicolo ${GlobalData.latoCorrente}.", style: const TextStyle(fontSize: 15)), const SizedBox(height: 16), _buildPopupRow(Icons.touch_app, "Punto d'urto", "Tocca direttamente sull'immagine per inserire una 'X' nel punto d'urto. Puoi selezionare più punti. Tocca di nuovo per rimuovere la 'X'."), const SizedBox(height: 12), _buildPopupRow(Icons.edit_document, "Danni Visibili", "Descrivi brevemente i danni visibili (es. 'Paraurti rotto')."), const SizedBox(height: 12), _buildPopupRow(Icons.comment, "Osservazioni", "Aggiungi eventuali commenti personali sulla dinamica (opzionale)."), ], ), ), actions: [ SizedBox( width: double.infinity, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: activeColor, foregroundColor: isB ? Colors.black87 : Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), padding: const EdgeInsets.symmetric(vertical: 14), ), onPressed: () => Navigator.pop(context), child: const Text("HO CAPITO", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), ), ), ], ); }, transitionBuilder: (context, animation, secondaryAnimation, child) { var curvePosizione = CurvedAnimation( parent: animation, curve: Curves.easeOutBack, reverseCurve: Curves.easeInBack, ); var curveOpacita = CurvedAnimation( parent: animation, curve: Curves.easeOut, reverseCurve: Curves.easeIn, ); return SlideTransition( position: Tween( begin: const Offset(0.0, 0.4), end: Offset.zero, ).animate(curvePosizione), child: FadeTransition( opacity: curveOpacita, child: child, ), ); }, ); } Widget _buildPopupRow(IconData icon, String title, String desc) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 24, color: Colors.blueGrey), const SizedBox(width: 12), Expanded( child: RichText( text: TextSpan( style: const TextStyle(fontSize: 14, color: Colors.black87, height: 1.4), children: [ TextSpan(text: "$title: ", style: const TextStyle(fontWeight: FontWeight.bold)), TextSpan(text: desc), ], ), ), ), ], ); } // --- MAPPATURA LATO A (BLU) --- Map _getHitboxesA() { return { '9': const Rect.fromLTWH(35, 85, 40, 40), '10': const Rect.fromLTWH(25, 110, 25, 50), '11': const Rect.fromLTWH(60, 110, 25, 50), '12': const Rect.fromLTWH(35, 175, 40, 40), '1': const Rect.fromLTWH(90, 50, 35, 40), '8': const Rect.fromLTWH(127, 50, 35, 40), '2': const Rect.fromLTWH(87, 90, 30, 60), '7': const Rect.fromLTWH(138, 90, 30, 60), '3': const Rect.fromLTWH(80, 150, 35, 40), '6': const Rect.fromLTWH(130, 150, 30, 40), '4': const Rect.fromLTWH(100, 190, 25, 30), '5': const Rect.fromLTWH(135, 190, 25, 30), '13': const Rect.fromLTWH(180, 35, 30, 40), '14': const Rect.fromLTWH(210, 35, 30, 40), '15': const Rect.fromLTWH(240, 35, 30, 40), '16': const Rect.fromLTWH(172, 75, 30, 70), '17': const Rect.fromLTWH(240, 75, 30, 70), '19': const Rect.fromLTWH(165, 145, 40, 60), '18': const Rect.fromLTWH(238, 145, 40, 60), '20': const Rect.fromLTWH(200, 195, 50, 25), }; } // --- MAPPATURA LATO B (GIALLO) --- Map _getHitboxesB() { return { 'M9': const Rect.fromLTWH(35, 78, 40, 40), 'M10': const Rect.fromLTWH(25, 110, 25, 50), 'M11': const Rect.fromLTWH(60, 110, 25, 50), 'M12': const Rect.fromLTWH(35, 175, 40, 40), '21': const Rect.fromLTWH(88, 50, 35, 40), '22': const Rect.fromLTWH(130, 50, 35, 40), '23': const Rect.fromLTWH(85, 90, 30, 60), '24': const Rect.fromLTWH(140, 90, 30, 60), '25': const Rect.fromLTWH(95, 190, 25, 30), '26': const Rect.fromLTWH(138, 190, 25, 30), '27': const Rect.fromLTWH(180, 35, 30, 40), '34': const Rect.fromLTWH(210, 35, 30, 40), '28': const Rect.fromLTWH(240, 35, 30, 40), '29': const Rect.fromLTWH(170, 75, 30, 90), '30': const Rect.fromLTWH(245, 75, 30, 90), '31': const Rect.fromLTWH(168, 171, 40, 60), '33': const Rect.fromLTWH(208, 178, 31, 60), '32': const Rect.fromLTWH(239, 171, 40, 60), }; } void _handleTap(TapUpDetails details) { Offset touchPosition = details.localPosition; String? foundSector; _hitboxes.forEach((key, rect) { if (rect.contains(touchPosition)) { foundSector = key; } }); if (foundSector != null) { setState(() { if (_selectedSectors.contains(foundSector)) { _selectedSectors.remove(foundSector); } else { _selectedSectors.add(foundSector!); } }); } } void _salvaEProsegui() { setState(() { if (isB) { GlobalData.puntiUrtoB_List = _selectedSectors.toList(); GlobalData.danni_visibili_B = _controllerDanni.text.toUpperCase(); GlobalData.osservazioni_B = _controllerOsservazioni.text.toUpperCase(); } else { GlobalData.puntiUrtoA_List = _selectedSectors.toList(); GlobalData.danni_visibili_A = _controllerDanni.text.toUpperCase(); GlobalData.osservazioni_A = _controllerOsservazioni.text.toUpperCase(); } }); Navigator.push(context, MaterialPageRoute(builder: (c) => const Comp12Screen())); } @override Widget build(BuildContext context) { Color mainCol = isB ? Colors.amber.shade700 : Colors.blue.shade900; Color bgCol = isB ? const Color(0xFFFFF9C4) : const Color(0xFFE3F2FD); return GestureDetector( onTap: () => FocusScope.of(context).unfocus(), child: Scaffold( backgroundColor: bgCol, appBar: AppBar( title: Text("10, 11, 14. Dettagli (${GlobalData.latoCorrente})"), backgroundColor: mainCol, foregroundColor: isB ? Colors.black : Colors.white, ), // --- SLIVER LAYOUT --- body: SafeArea( child: CustomScrollView( physics: const BouncingScrollPhysics(), slivers: [ SliverPadding( padding: const EdgeInsets.all(16), sliver: SliverList( delegate: SliverChildListDelegate([ _buildTitle("10. PUNTO D'URTO INIZIALE (Seleziona anche più di uno)", mainCol), const SizedBox(height: 10), Center( child: Card( elevation: 4, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), child: GestureDetector( onTapUp: _handleTap, child: Container( width: _canvasWidth, height: _canvasHeight, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15)), child: Stack( children: [ Center(child: Image.asset( isB ? 'assets/punti_danni_B.png' : 'assets/punti_danni_A.png', width: _canvasWidth, height: _canvasHeight, fit: BoxFit.contain, )), ..._selectedSectors.map((sectorId) { if (_hitboxes.containsKey(sectorId)) { return CustomPaint( painter: XPainter(_hitboxes[sectorId]!), size: Size(_canvasWidth, _canvasHeight), ); } return Container(); }), ], ), ), ), ), ), if (_selectedSectors.isNotEmpty) Padding( padding: const EdgeInsets.only(top: 8.0), child: Text("Punti: ${_selectedSectors.join(', ')}", style: TextStyle(color: mainCol, fontWeight: FontWeight.bold)), ), const Divider(height: 30), _buildTitle("11. DANNI VISIBILI", mainCol), TextField( controller: _controllerDanni, // Max 45 caratteri per stare nelle 2 righe del PDF (20+25) maxLength: 45, maxLines: 2, keyboardType: TextInputType.multiline, textCapitalization: TextCapitalization.characters, decoration: _inputDeco("Es: Paraurti ant, Faro sx..."), ), const SizedBox(height: 10), _buildTitle("14. OSSERVAZIONI", mainCol), TextField( controller: _controllerOsservazioni, maxLength: 55, maxLines: 2, keyboardType: TextInputType.multiline, textCapitalization: TextCapitalization.sentences, decoration: _inputDeco("Es: Ho ragione io perché..."), ), ]), ), ), // --- STICKY FOOTER --- SliverFillRemaining( hasScrollBody: false, child: Align( alignment: Alignment.bottomCenter, child: Container( padding: const EdgeInsets.all(16), child: _navButtons(context, mainCol), ), ), ), ], ), ), ), ); } Widget _buildTitle(String text, Color color) { return Align(alignment: Alignment.centerLeft, child: Padding( padding: const EdgeInsets.only(bottom: 8.0, top: 10.0), child: Text(text, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: color)), )); } InputDecoration _inputDeco(String hint) { return InputDecoration( hintText: hint, filled: true, fillColor: Colors.white, border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)), contentPadding: const EdgeInsets.all(15), // counterText: "", <-- RIMOSSO: Ora il contatore si vede! ); } Widget _navButtons(BuildContext context, Color color) { return Row( children: [ Expanded( flex: 4, child: OutlinedButton( onPressed: () => Navigator.pop(context), style: OutlinedButton.styleFrom( minimumSize: const Size(0, 55), padding: const EdgeInsets.symmetric(horizontal: 5), ), child: const FittedBox( child: Text("INDIETRO", style: TextStyle(fontWeight: FontWeight.bold)) ) ) ), const SizedBox(width: 15), Expanded( flex: 6, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: color, foregroundColor: isB ? Colors.black : Colors.white, minimumSize: const Size(0, 55) ), onPressed: _salvaEProsegui, child: const FittedBox( child: Text("SALVA E PROSEGUI", style: TextStyle(fontWeight: FontWeight.bold)) ) ) ), ], ); } } class XPainter extends CustomPainter { final Rect targetRect; XPainter(this.targetRect); @override void paint(Canvas canvas, Size size) { final Paint paint = Paint()..color = Colors.red..strokeWidth = 3.0..strokeCap = StrokeCap.round..style = PaintingStyle.stroke; double cx = targetRect.left + targetRect.width / 2; double cy = targetRect.top + targetRect.height / 2; double iconSize = 10.0; canvas.drawLine(Offset(cx - iconSize, cy - iconSize), Offset(cx + iconSize, cy + iconSize), paint); canvas.drawLine(Offset(cx + iconSize, cy - iconSize), Offset(cx - iconSize, cy + iconSize), paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; }