403 lines
No EOL
15 KiB
Dart
403 lines
No EOL
15 KiB
Dart
// 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<Comp10Screen> {
|
|
// Set per gestire selezioni multiple uniche
|
|
Set<String> _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<String, Rect> _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<Offset>(
|
|
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<String, Rect> _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<String, Rect> _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;
|
|
} |