2026-02-27 23:26:13 +01:00
// Versione: 2.3.5 - Popup Automatico + Calendari Cliccabili
import ' package:flutter/material.dart ' ;
import ' package:flutter/services.dart ' ;
import ' global_data.dart ' ;
import ' comp_10.dart ' ;
2026-04-28 16:00:03 +02:00
import ' services/profilo_service.dart ' ;
2026-02-27 23:26:13 +01:00
class DateInputFormatter extends TextInputFormatter {
@ override
TextEditingValue formatEditUpdate ( TextEditingValue oldValue , TextEditingValue newValue ) {
var text = newValue . text . replaceAll ( ' / ' , ' ' ) ;
if ( text . length > 8 ) text = text . substring ( 0 , 8 ) ;
var result = " " ;
for ( int i = 0 ; i < text . length ; i + + ) {
if ( i = = 2 | | i = = 4 ) result + = " / " ;
result + = text [ i ] ;
}
return newValue . copyWith (
text: result ,
selection: TextSelection . collapsed ( offset: result . length )
) ;
}
}
class Comp9Screen extends StatefulWidget {
const Comp9Screen ( { super . key } ) ;
@ override
_Comp9ScreenState createState ( ) = > _Comp9ScreenState ( ) ;
}
class _Comp9ScreenState extends State < Comp9Screen > {
2026-04-28 20:20:45 +02:00
late TextEditingController _cognome , _nome , _cf , _nascita , _indirizzo , _stato , _tel , _email , _patente , _categoriaAltro , _scadenza ;
2026-02-27 23:26:13 +01:00
String _selectedCat = " B " ;
String ? _erroreNascita ;
String ? _erroreScadenza ;
bool get isB = > GlobalData . latoCorrente = = ' B ' ;
@ override
void initState ( ) {
super . initState ( ) ;
_initControllers ( ) ;
// Appena la pagina è costruita, lancia il controllo per i popup
WidgetsBinding . instance . addPostFrameCallback ( ( _ ) {
// PRIMA mostra le info della pagina...
_mostraInfoPopup ( context ) ;
// ...POI controlla se ci sono date pre-compilate da validare
if ( _nascita . text . isNotEmpty ) _validaNascita ( _nascita . text ) ;
if ( _scadenza . text . isNotEmpty ) _validaScadenza ( _scadenza . text ) ;
} ) ;
}
void _initControllers ( ) {
if ( isB ) {
2026-05-02 12:00:02 +02:00
_cognome = TextEditingController ( text: GlobalData . cognomeCondB ) ;
_nome = TextEditingController ( text: GlobalData . nomeCondB ) ;
_cf = TextEditingController ( text: GlobalData . codFiscaleCondB ) ;
_nascita = TextEditingController ( text: GlobalData . dataNascitaCondB ) ;
_indirizzo = TextEditingController ( text: GlobalData . indirizzoCondB ) ;
_stato = TextEditingController ( text: GlobalData . statoCondB . isEmpty ? " ITALIA " : GlobalData . statoCondB ) ;
_tel = TextEditingController ( text: GlobalData . nTelMailCondB ) ;
_email = TextEditingController ( text: GlobalData . emailCondB ) ;
_patente = TextEditingController ( text: GlobalData . nPatenteCondB ) ;
_scadenza = TextEditingController ( text: GlobalData . scadenzaCondB ) ;
_setupCategoria ( GlobalData . categoriaCondB ) ;
2026-02-27 23:26:13 +01:00
} else {
2026-05-02 12:00:02 +02:00
_cognome = TextEditingController ( text: GlobalData . cognomeCondA ) ;
_nome = TextEditingController ( text: GlobalData . nomeCondA ) ;
_cf = TextEditingController ( text: GlobalData . codFiscaleCondA ) ;
_nascita = TextEditingController ( text: GlobalData . dataNascitaCondA ) ;
_indirizzo = TextEditingController ( text: GlobalData . indirizzoCondA ) ;
_stato = TextEditingController ( text: GlobalData . statoCondA . isEmpty ? " ITALIA " : GlobalData . statoCondA ) ;
_tel = TextEditingController ( text: GlobalData . nTelMailCondA ) ;
_email = TextEditingController ( text: GlobalData . emailCondA ) ;
_patente = TextEditingController ( text: GlobalData . nPatenteCondA ) ;
_scadenza = TextEditingController ( text: GlobalData . scadenzaCondA ) ;
_setupCategoria ( GlobalData . categoriaCondA ) ;
2026-02-27 23:26:13 +01:00
}
}
// --- POPUP INFORMATIVO ANIMATO ---
void _mostraInfoPopup ( BuildContext context ) {
Color activeColor = isB ? Colors . amber . shade700 : Colors . blue . shade900 ;
showGeneralDialog (
context: context ,
barrierDismissible: false ,
barrierLabel: " Popup " ,
2026-04-24 23:00:16 +02:00
barrierColor: Colors . black . withValues ( alpha: 0.5 ) ,
2026-02-27 23:26:13 +01:00
transitionDuration: const Duration ( milliseconds: 400 ) ,
pageBuilder: ( context , animation , secondaryAnimation ) {
return AlertDialog (
shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 20 ) ) ,
title: Row (
children: [
Icon ( Icons . person_pin , color: activeColor , size: 28 ) ,
const SizedBox ( width: 10 ) ,
const Expanded ( child: Text ( " Dati Conducente " , style: TextStyle ( fontWeight: FontWeight . bold , fontSize: 18 ) ) ) ,
] ,
) ,
content: SingleChildScrollView (
child: Column (
crossAxisAlignment: CrossAxisAlignment . start ,
mainAxisSize: MainAxisSize . min ,
children: [
Text ( " In questa sezione inseriremo i dati della persona che era alla guida del Veicolo ${ GlobalData . latoCorrente } . " , style: const TextStyle ( fontSize: 15 ) ) ,
const SizedBox ( height: 16 ) ,
_buildPopupRow ( Icons . file_download , " Importazione " , " Se il conducente è la stessa persona del contraente (l'assicurato), potrai importare i suoi dati in un tocco. " ) ,
const SizedBox ( height: 12 ) ,
_buildPopupRow ( Icons . badge , " Patente " , " Assicurati di inserire correttamente il numero, la categoria e la data di scadenza della patente di guida. " ) ,
] ,
) ,
) ,
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 ) ; // Chiude l'info popup
// Dopo aver chiuso questo, controlla se deve mostrare l'altro popup per l'importazione
_mostraDialogoImportazione ( ) ;
} ,
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 ) ,
] ,
) ,
) ,
) ,
] ,
) ;
}
// LOGICA POPUP AUTOMATICO
void _mostraDialogoImportazione ( ) {
// 1. Se i campi sono già compilati (es. torno indietro per modificare), NON mostrare nulla
if ( _cognome . text . trim ( ) . isNotEmpty & & _nome . text . trim ( ) . isNotEmpty ) return ;
// 2. Controllo se esistono i dati del contraente da importare
2026-05-02 12:00:02 +02:00
String contraenteNome = isB ? GlobalData . nomeContraenteB : GlobalData . nomeContraenteA ;
String contraenteCognome = isB ? GlobalData . cognomeContraenteB : GlobalData . cognomeContraenteA ;
2026-02-27 23:26:13 +01:00
if ( contraenteNome . isEmpty & & contraenteCognome . isEmpty ) return ;
// 3. Mostra il Popup
showDialog (
context: context ,
barrierDismissible: false , // L'utente deve fare una scelta
builder: ( ctx ) = > AlertDialog (
shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 15 ) ) ,
title: Row (
children: [
Icon ( Icons . person_add_alt_1 , color: isB ? Colors . amber [ 800 ] : Colors . blue [ 800 ] ) ,
const SizedBox ( width: 10 ) ,
const Text ( " Conducente " ) ,
] ,
) ,
content: Text (
" Il conducente è la stessa persona del contraente ( $ contraenteNome $ contraenteCognome )? \n \n Vuoi usare i suoi dati? " ,
style: const TextStyle ( fontSize: 16 ) ,
) ,
actions: [
TextButton (
onPressed: ( ) = > Navigator . pop ( ctx ) ,
child: Text ( " NO, scrivo a mano " , style: TextStyle ( color: Colors . grey [ 600 ] ) ) ,
) ,
ElevatedButton (
style: ElevatedButton . styleFrom (
backgroundColor: isB ? Colors . amber [ 700 ] : Colors . blue . shade900 ,
foregroundColor: Colors . white ,
shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 8 ) ) ,
) ,
onPressed: ( ) {
_importaDati ( ) ;
Navigator . pop ( ctx ) ;
} ,
child: const Text ( " SÌ, importa " ) ,
) ,
] ,
) ,
) ;
}
void _importaDati ( ) {
setState ( ( ) {
if ( isB ) {
2026-05-02 12:00:02 +02:00
_cognome . text = GlobalData . cognomeContraenteB ;
_nome . text = GlobalData . nomeContraenteB ;
_cf . text = GlobalData . codiceFiscaleContraenteB ;
2026-02-27 23:26:13 +01:00
2026-05-02 12:00:02 +02:00
String addr = GlobalData . indirizzoContraenteB ;
if ( GlobalData . capContraenteB . isNotEmpty ) addr + = " ${ GlobalData . capContraenteB } " ;
2026-02-27 23:26:13 +01:00
_indirizzo . text = addr . trim ( ) ;
2026-05-02 12:00:02 +02:00
_stato . text = GlobalData . statoContraenteB . isNotEmpty ? GlobalData . statoContraenteB : " ITALIA " ;
_tel . text = GlobalData . nTelefonoMailContraenteB ;
_email . text = GlobalData . emailContraenteB ;
2026-02-27 23:26:13 +01:00
} else {
2026-05-02 12:00:02 +02:00
_cognome . text = GlobalData . cognomeContraenteA ;
_nome . text = GlobalData . nomeContraenteA ;
_cf . text = GlobalData . codiceFiscaleContraenteA ;
2026-02-27 23:26:13 +01:00
2026-05-02 12:00:02 +02:00
String addr = GlobalData . indirizzoContraenteA ;
if ( GlobalData . capContraenteA . isNotEmpty ) addr + = " ${ GlobalData . capContraenteA } " ;
2026-02-27 23:26:13 +01:00
_indirizzo . text = addr . trim ( ) ;
2026-05-02 12:00:02 +02:00
_stato . text = GlobalData . statoContraenteA . isNotEmpty ? GlobalData . statoContraenteA : " ITALIA " ;
_tel . text = GlobalData . nTelefonoMailContraenteA ;
_email . text = GlobalData . emailContraenteA ;
2026-02-27 23:26:13 +01:00
}
} ) ;
}
void _setupCategoria ( String catEsistente ) {
if ( [ " A " , " B " , " C " , " D " , " E " ] . contains ( catEsistente ) ) {
_selectedCat = catEsistente ;
_categoriaAltro = TextEditingController ( ) ;
} else if ( catEsistente . isNotEmpty ) {
_selectedCat = " Altro " ;
_categoriaAltro = TextEditingController ( text: catEsistente ) ;
} else {
_selectedCat = " B " ;
_categoriaAltro = TextEditingController ( ) ;
}
}
// --- LOGICA CALENDARIO ---
Future < void > _selezionaData ( BuildContext context , TextEditingController controller , Function ( String ) onChanged ) async {
DateTime initialDate = DateTime . now ( ) ;
if ( controller . text . length = = 10 ) {
try {
List < String > parti = controller . text . split ( ' / ' ) ;
initialDate = DateTime ( int . parse ( parti [ 2 ] ) , int . parse ( parti [ 1 ] ) , int . parse ( parti [ 0 ] ) ) ;
} catch ( _ ) { }
}
final DateTime ? picked = await showDatePicker (
context: context ,
initialDate: initialDate ,
firstDate: DateTime ( 1900 ) , // Anno minimo sensato per la nascita
lastDate: DateTime ( 2100 ) ,
locale: const Locale ( ' it ' , ' IT ' ) ,
) ;
if ( picked ! = null ) {
String dataFormattata = " ${ picked . day . toString ( ) . padLeft ( 2 , ' 0 ' ) } / ${ picked . month . toString ( ) . padLeft ( 2 , ' 0 ' ) } / ${ picked . year } " ;
setState ( ( ) {
controller . text = dataFormattata ;
onChanged ( dataFormattata ) ;
} ) ;
}
}
String ? _calcolaErroreData ( String data ) {
if ( data . isEmpty ) return null ;
if ( data . length < 10 ) return " Formato: GG/MM/AAAA " ;
List < String > parti = data . split ( ' / ' ) ;
if ( parti . length ! = 3 ) return " Formato errato " ;
int ? giorno = int . tryParse ( parti [ 0 ] ) ;
int ? mese = int . tryParse ( parti [ 1 ] ) ;
int ? anno = int . tryParse ( parti [ 2 ] ) ;
if ( giorno = = null | | mese = = null | | anno = = null ) return " Data non valida " ;
if ( giorno < 1 | | giorno > 31 ) return " Giorno errato " ;
if ( mese < 1 | | mese > 12 ) return " Mese errato " ;
int giorniMax = _giorniInMese ( mese , anno ) ;
if ( giorno > giorniMax ) {
String nomeMese = _getNomeMese ( mese ) ;
return " $ nomeMese $ anno ha solo $ giorniMax gg " ;
}
return null ;
}
int _giorniInMese ( int mese , int anno ) {
if ( mese = = 2 ) {
bool bisestile = ( anno % 4 = = 0 & & anno % 100 ! = 0 ) | | ( anno % 400 = = 0 ) ;
return bisestile ? 29 : 28 ;
}
if ( [ 4 , 6 , 9 , 11 ] . contains ( mese ) ) return 30 ;
return 31 ;
}
String _getNomeMese ( int mese ) {
const mesi = [ " Gen " , " Feb " , " Mar " , " Apr " , " Mag " , " Giu " , " Lug " , " Ago " , " Set " , " Ott " , " Nov " , " Dic " ] ;
if ( mese > = 1 & & mese < = 12 ) return mesi [ mese - 1 ] ;
return " Mese " ;
}
void _validaNascita ( String val ) = > setState ( ( ) = > _erroreNascita = _calcolaErroreData ( val ) ) ;
void _validaScadenza ( String val ) = > setState ( ( ) = > _erroreScadenza = _calcolaErroreData ( val ) ) ;
void _salvaEProsegui ( ) {
String catFinale = ( _selectedCat = = " Altro " ) ? _categoriaAltro . text . toUpperCase ( ) : _selectedCat ;
bool categoriaMancante = ( _selectedCat = = " Altro " & & _categoriaAltro . text . trim ( ) . isEmpty ) ;
2026-04-28 20:20:45 +02:00
if ( _cognome . text . trim ( ) . isEmpty | | _nome . text . trim ( ) . isEmpty | |
_nascita . text . trim ( ) . isEmpty | | _indirizzo . text . trim ( ) . isEmpty | |
_stato . text . trim ( ) . isEmpty | | _tel . text . trim ( ) . isEmpty | |
_patente . text . trim ( ) . isEmpty | | _scadenza . text . trim ( ) . isEmpty ) {
ScaffoldMessenger . of ( context ) . showSnackBar ( const SnackBar ( content: Text ( " Compila tutti i campi obbligatori! " ) , backgroundColor: Colors . red ) ) ;
return ;
}
// Validazione Telefono
String telefono = _tel . text . trim ( ) ;
if ( ! RegExp ( r'^\+?[0-9\s\-\.]+$' ) . hasMatch ( telefono ) ) {
ScaffoldMessenger . of ( context ) . showSnackBar ( const SnackBar ( content: Text ( " Numero di telefono non valido. Usa solo numeri e il prefisso +. " ) , backgroundColor: Colors . orange ) ) ;
return ;
}
// Validazione Email (se inserita)
String email = _email . text . trim ( ) ;
if ( email . isNotEmpty & & ! RegExp ( r'^[a-zA-Z0-9.]+@[a-zA-Z0-9]+\.[a-zA-Z]+' ) . hasMatch ( email ) ) {
ScaffoldMessenger . of ( context ) . showSnackBar ( const SnackBar ( content: Text ( " Formato email non valido! " ) , backgroundColor: Colors . orange ) ) ;
2026-02-27 23:26:13 +01:00
return ;
}
if ( _erroreNascita ! = null | | _erroreScadenza ! = null ) {
_mostraErrore ( " Correggi le date in rosso! " , Colors . red ) ;
return ;
}
if ( _nascita . text . length < 10 | | _scadenza . text . length < 10 ) {
_mostraErrore ( " Formato data incompleto (GG/MM/AAAA) " , Colors . orange ) ;
return ;
}
if ( isB ) {
2026-05-02 12:00:02 +02:00
GlobalData . cognomeCondB = _cognome . text . toUpperCase ( ) ;
GlobalData . nomeCondB = _nome . text . toUpperCase ( ) ;
GlobalData . codFiscaleCondB = _cf . text . toUpperCase ( ) ;
GlobalData . dataNascitaCondB = _nascita . text ;
GlobalData . indirizzoCondB = _indirizzo . text . trim ( ) . toUpperCase ( ) ;
GlobalData . statoCondB = _stato . text . trim ( ) . toUpperCase ( ) ;
GlobalData . nTelMailCondB = telefono ;
GlobalData . emailCondB = email . toLowerCase ( ) ;
GlobalData . nPatenteCondB = _patente . text . trim ( ) . toUpperCase ( ) ;
GlobalData . categoriaCondB = catFinale ;
GlobalData . scadenzaCondB = _scadenza . text ;
2026-02-27 23:26:13 +01:00
} else {
2026-05-02 12:00:02 +02:00
GlobalData . cognomeCondA = _cognome . text . toUpperCase ( ) ;
GlobalData . nomeCondA = _nome . text . toUpperCase ( ) ;
GlobalData . codFiscaleCondA = _cf . text . toUpperCase ( ) ;
GlobalData . dataNascitaCondA = _nascita . text ;
GlobalData . indirizzoCondA = _indirizzo . text . trim ( ) . toUpperCase ( ) ;
GlobalData . statoCondA = _stato . text . trim ( ) . toUpperCase ( ) ;
GlobalData . nTelMailCondA = telefono ;
GlobalData . emailCondA = email . toLowerCase ( ) ;
GlobalData . nPatenteCondA = _patente . text . trim ( ) . toUpperCase ( ) ;
GlobalData . categoriaCondA = catFinale ;
GlobalData . scadenzaCondA = _scadenza . text ;
2026-02-27 23:26:13 +01:00
}
2026-04-28 16:00:03 +02:00
// Salvataggio silente del profilo utente
ProfiloService . salvaProfilo ( GlobalData . latoCorrente ) ;
2026-02-27 23:26:13 +01:00
Navigator . push ( context , MaterialPageRoute ( builder: ( c ) = > const Comp10Screen ( ) ) ) ;
}
void _mostraErrore ( String msg , Color colore ) {
ScaffoldMessenger . of ( context ) . showSnackBar (
SnackBar ( content: Text ( msg ) , backgroundColor: colore , duration: const Duration ( seconds: 3 ) ) ,
) ;
}
@ override
Widget build ( BuildContext context ) {
Color accentColor = isB ? Colors . amber . shade700 : Colors . blue . shade900 ;
Color bgColor = isB ? const Color ( 0xFFFFF9C4 ) : const Color ( 0xFFE3F2FD ) ;
return GestureDetector (
onTap: ( ) = > FocusScope . of ( context ) . unfocus ( ) ,
child: Scaffold (
backgroundColor: bgColor ,
resizeToAvoidBottomInset: true ,
appBar: AppBar (
title: Text ( " 9. Conducente ( ${ GlobalData . latoCorrente } ) " ) ,
backgroundColor: accentColor ,
foregroundColor: isB ? Colors . black : Colors . white ,
) ,
body: SafeArea (
child: SingleChildScrollView (
physics: const BouncingScrollPhysics ( ) ,
padding: const EdgeInsets . all ( 16 ) ,
child: Column (
children: [
_buildSectionCard (
titolo: " DATI PERSONALI " ,
accentColor: accentColor ,
children: [
_buildTextField ( _cognome , " Cognome * " , Icons . person , accentColor ) ,
_buildTextField ( _nome , " Nome * " , Icons . person_outline , accentColor ) ,
// --- MODIFICATO: DATA DI NASCITA ---
_buildTextField ( _nascita , " Data di Nascita * " , Icons . cake , accentColor ,
isDate: true , hint: " GG/MM/AAAA " , errorText: _erroreNascita , onChanged: _validaNascita ,
customPrefix: InkWell (
onTap: ( ) = > _selezionaData ( context , _nascita , _validaNascita ) ,
borderRadius: BorderRadius . circular ( 20 ) ,
child: Container (
width: 38 ,
alignment: Alignment . center ,
child: Icon ( Icons . cake , size: 20 , color: accentColor ) ,
) ,
) ,
) ,
_buildTextField ( _cf , " Codice Fiscale * " , Icons . badge , accentColor , isUpper: true ) ,
_buildTextField ( _indirizzo , " Indirizzo (Via, Cap, Città) * " , Icons . home , accentColor ) ,
2026-04-28 20:20:45 +02:00
_buildTextField ( _stato , " Stato di Residenza * " , Icons . public , accentColor , isUpper: true ) ,
_buildTextField ( _tel , " Telefono * " , Icons . phone , accentColor ) ,
_buildTextField ( _email , " Email (Opzionale) " , Icons . email , accentColor ) ,
2026-02-27 23:26:13 +01:00
] ,
) ,
_buildSectionCard (
titolo: " PATENTE DI GUIDA " ,
accentColor: accentColor ,
children: [
_buildTextField ( _patente , " N. Patente * " , Icons . credit_card , accentColor , isUpper: true ) ,
Padding (
padding: const EdgeInsets . only ( bottom: 12 ) ,
child: DropdownButtonFormField < String > (
value: _selectedCat ,
decoration: InputDecoration (
labelText: " Categoria * " ,
prefixIcon: Icon ( Icons . category , color: accentColor ) ,
border: OutlineInputBorder ( borderRadius: BorderRadius . circular ( 10 ) ) ,
filled: true , fillColor: Colors . white ,
) ,
items: [ " A " , " B " , " C " , " D " , " E " , " Altro " ] . map ( ( c ) = > DropdownMenuItem ( value: c , child: Text ( c ) ) ) . toList ( ) ,
onChanged: ( v ) = > setState ( ( ) = > _selectedCat = v ! ) ,
) ,
) ,
if ( _selectedCat = = " Altro " )
_buildTextField ( _categoriaAltro , " Specifica Categoria * " , Icons . edit , accentColor , isUpper: true ) ,
// --- MODIFICATO: SCADENZA PATENTE ---
_buildTextField ( _scadenza , " Valida fino al * " , Icons . event_available , accentColor ,
isDate: true , hint: " GG/MM/AAAA " , errorText: _erroreScadenza , onChanged: _validaScadenza ,
customPrefix: InkWell (
onTap: ( ) = > _selezionaData ( context , _scadenza , _validaScadenza ) ,
borderRadius: BorderRadius . circular ( 20 ) ,
child: Container (
width: 38 ,
alignment: Alignment . center ,
child: Icon ( Icons . event_available , size: 20 , color: accentColor ) ,
) ,
) ,
) ,
] ,
) ,
_navButtons ( accentColor ) ,
const SizedBox ( height: 10 ) ,
] ,
) ,
) ,
) ,
) ,
) ;
}
// --- MODIFICATO IL METODO PER ACCETTARE customPrefix E RESTRINGERE LA UI ---
Widget _buildTextField ( TextEditingController c , String label , IconData icon , Color color ,
{ bool isDate = false , bool isUpper = false , bool isNumeric = false , String ? hint , String ? errorText , Function ( String ) ? onChanged , Widget ? customPrefix } ) {
return Padding (
padding: const EdgeInsets . only ( bottom: 12 ) ,
child: TextField (
controller: c ,
keyboardType: isDate | | isNumeric ? TextInputType . number : TextInputType . text ,
textCapitalization: isUpper ? TextCapitalization . characters : TextCapitalization . sentences ,
onChanged: onChanged ,
style: TextStyle ( fontSize: isDate ? 14 : 16 ) , // Rimpicciolisce un po' le date
inputFormatters: [
if ( isDate ) DateInputFormatter ( ) ,
if ( isDate ) LengthLimitingTextInputFormatter ( 10 ) ,
if ( isNumeric ) FilteringTextInputFormatter . digitsOnly ,
if ( isNumeric ) LengthLimitingTextInputFormatter ( 5 ) ,
] ,
decoration: InputDecoration (
labelText: label ,
hintText: hint ,
errorText: errorText ,
errorStyle: const TextStyle ( fontSize: 10 ) ,
prefixIcon: customPrefix ? ? Icon ( icon , color: color ) ,
prefixIconConstraints: isDate ? const BoxConstraints ( minWidth: 38 , minHeight: 38 ) : null ,
contentPadding: const EdgeInsets . symmetric ( horizontal: 12 , vertical: 15 ) ,
border: OutlineInputBorder ( borderRadius: BorderRadius . circular ( 10 ) ) ,
filled: true , fillColor: Colors . white ,
) ,
) ,
) ;
}
Widget _buildSectionCard ( { required String titolo , required List < Widget > children , required Color accentColor } ) = > Container (
width: double . infinity , margin: const EdgeInsets . only ( bottom: 20 ) , padding: const EdgeInsets . all ( 16 ) ,
2026-04-24 23:00:16 +02:00
decoration: BoxDecoration ( color: Colors . white , borderRadius: BorderRadius . circular ( 15 ) , boxShadow: [ BoxShadow ( color: Colors . black . withValues ( alpha: 0.05 ) , blurRadius: 10 ) ] ) ,
2026-02-27 23:26:13 +01:00
child: Column ( crossAxisAlignment: CrossAxisAlignment . start , children: [
Text ( titolo , style: TextStyle ( fontWeight: FontWeight . bold , color: accentColor , fontSize: 17 ) ) ,
const Divider ( height: 25 ) , . . . children ,
] ) ,
) ;
Widget _navButtons ( Color btnColor ) = > Row ( children: [
Expanded ( flex: 4 , child: OutlinedButton ( onPressed: ( ) = > Navigator . pop ( context ) , style: OutlinedButton . styleFrom ( minimumSize: const Size ( 0 , 55 ) , side: BorderSide ( color: btnColor , width: 1.5 ) , shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 15 ) ) ) , child: const FittedBox ( child: Text ( " INDIETRO " , style: TextStyle ( fontWeight: FontWeight . bold ) ) ) ) ) ,
const SizedBox ( width: 15 ) ,
Expanded ( flex: 7 , child: ElevatedButton ( style: ElevatedButton . styleFrom ( backgroundColor: btnColor , foregroundColor: isB ? Colors . black : Colors . white , minimumSize: const Size ( 0 , 55 ) , elevation: 5 , shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 15 ) ) ) , onPressed: _salvaEProsegui , child: const FittedBox ( child: Text ( " SALVA E PROSEGUI " , style: TextStyle ( fontWeight: FontWeight . bold , fontSize: 16 ) ) ) ) ) ,
] ) ;
@ override
void dispose ( ) {
2026-04-28 20:20:45 +02:00
_cognome . dispose ( ) ; _nome . dispose ( ) ; _cf . dispose ( ) ;
_nascita . dispose ( ) ; _indirizzo . dispose ( ) ; _stato . dispose ( ) ;
_tel . dispose ( ) ; _email . dispose ( ) ; _patente . dispose ( ) ; _categoriaAltro . dispose ( ) ; _scadenza . dispose ( ) ;
2026-02-27 23:26:13 +01:00
super . dispose ( ) ;
}
}