688 lines
No EOL
27 KiB
Dart
688 lines
No EOL
27 KiB
Dart
import 'dart:io';
|
|
import 'dart:async';
|
|
import 'dart:typed_data';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_email_sender/flutter_email_sender.dart';
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'package:printing/printing.dart';
|
|
import 'package:share_plus/share_plus.dart';
|
|
|
|
import 'scambio_dati_screen.dart';
|
|
import 'pdf_engine.dart';
|
|
import 'global_data.dart';
|
|
import 'main.dart';
|
|
import 'comp_6-7.dart';
|
|
import 'comp_1-5.dart';
|
|
|
|
class Comp16Screen extends StatefulWidget {
|
|
const Comp16Screen({super.key});
|
|
|
|
@override
|
|
State<Comp16Screen> createState() => _Comp16ScreenState();
|
|
}
|
|
|
|
class _Comp16ScreenState extends State<Comp16Screen> with WidgetsBindingObserver {
|
|
bool _scambioEffettuato = false;
|
|
bool _datiPresenti = false;
|
|
bool _ioHoApprovato = false;
|
|
bool _tuttiHannoApprovato = false;
|
|
|
|
bool _staCancellando = false;
|
|
bool _cancellazioneAvviataDaMe = false;
|
|
|
|
String _statusText = "Esegui lo Scambio Dati per iniziare.";
|
|
Color _statusColor = Colors.orange.shade800;
|
|
IconData _statusIcon = Icons.warning_amber_rounded;
|
|
|
|
File? _filePdfReale;
|
|
Uint8List? _immagineAnteprima;
|
|
bool _isLoading = false;
|
|
StreamSubscription? _roomSubscription;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addObserver(this);
|
|
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
|
_puliziaIngresso();
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
_mostraInfoPopup(context);
|
|
});
|
|
}
|
|
|
|
void _mostraInfoPopup(BuildContext context) {
|
|
Color activeColor = 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.sync_alt, color: activeColor, size: 28),
|
|
const SizedBox(width: 10),
|
|
const Expanded(child: Text("Scambio e Invio", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18))),
|
|
],
|
|
),
|
|
content: SingleChildScrollView(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Text("Questa è la fase finale. Segui questi tre passaggi per concludere il modulo:", style: TextStyle(fontSize: 15)),
|
|
const SizedBox(height: 16),
|
|
_buildPopupRow(Icons.qr_code_scanner, "1. Scambio Dati", "Inquadra il QR Code dell'altro conducente oppure inserisci a mano il suo codice PIN."),
|
|
const SizedBox(height: 12),
|
|
_buildPopupRow(Icons.visibility, "2. Anteprima", "Apri l'anteprima per verificare che i dati di entrambi siano impaginati correttamente sul documento."),
|
|
const SizedBox(height: 12),
|
|
_buildPopupRow(Icons.check_circle, "3. Approvazione", "Se tutto è esatto, clicca su Approva. Quando entrambi avrete approvato, il file sarà pronto per l'invio."),
|
|
],
|
|
),
|
|
),
|
|
actions: [
|
|
SizedBox(
|
|
width: double.infinity,
|
|
child: ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: activeColor,
|
|
foregroundColor: 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),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Future<void> _puliziaIngresso() async {
|
|
if (GlobalData.idScambioTemporaneo == null && GlobalData.idSessione != null) {
|
|
GlobalData.idScambioTemporaneo = GlobalData.idSessione;
|
|
}
|
|
if (GlobalData.idScambioTemporaneo == null) {
|
|
if (GlobalData.latoCorrente == 'A') GlobalData.resetB(); else GlobalData.resetA();
|
|
}
|
|
if (mounted) _verificaStatoPostScambio();
|
|
}
|
|
|
|
void _verificaStatoPostScambio() {
|
|
bool datiOk = false;
|
|
if (GlobalData.latoCorrente == 'A') {
|
|
datiOk = GlobalData.Cognome_contraente_B.trim().isNotEmpty && GlobalData.Targa_B.trim().isNotEmpty;
|
|
} else {
|
|
datiOk = GlobalData.Cognome_contraente_A.trim().isNotEmpty && GlobalData.Targa_A.trim().isNotEmpty;
|
|
}
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
if (datiOk) {
|
|
_scambioEffettuato = true;
|
|
_datiPresenti = true;
|
|
_statusText = "Dati ricevuti. Generazione anteprima...";
|
|
_statusColor = Colors.blue.shade800;
|
|
_statusIcon = Icons.pending_actions;
|
|
_generaDocumenti();
|
|
_attivaAscoltoStanza();
|
|
} else {
|
|
_resetStatiUI();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void _resetStatiUI() {
|
|
_scambioEffettuato = false;
|
|
_datiPresenti = false;
|
|
_ioHoApprovato = false;
|
|
_tuttiHannoApprovato = false;
|
|
_statusText = "Esegui lo Scambio Dati per iniziare.";
|
|
_statusColor = Colors.orange.shade800;
|
|
_statusIcon = Icons.warning_amber_rounded;
|
|
_filePdfReale = null;
|
|
_immagineAnteprima = null;
|
|
}
|
|
|
|
void _attivaAscoltoStanza() {
|
|
String? idDaAscoltare = GlobalData.idScambioTemporaneo ?? GlobalData.idSessione;
|
|
if (idDaAscoltare == null || _roomSubscription != null) return;
|
|
|
|
_roomSubscription = FirebaseFirestore.instance
|
|
.collection('scambi_cid')
|
|
.doc(idDaAscoltare)
|
|
.snapshots()
|
|
.listen((snapshot) async {
|
|
|
|
if (!snapshot.exists) {
|
|
if (_ioHoApprovato) {
|
|
_roomSubscription?.cancel();
|
|
_roomSubscription = null;
|
|
return;
|
|
}
|
|
if (!_staCancellando && !_cancellazioneAvviataDaMe && mounted) {
|
|
_gestisciCancellazioneAltrui();
|
|
}
|
|
return;
|
|
}
|
|
|
|
final data = snapshot.data();
|
|
if (data == null) return;
|
|
|
|
if (data['status'] == 'retry') {
|
|
if (!_cancellazioneAvviataDaMe) _gestisciCancellazioneAltrui();
|
|
return;
|
|
}
|
|
|
|
bool appA = data['approved_A'] == true;
|
|
bool appB = data['approved_B'] == true;
|
|
|
|
if (appA && appB) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_tuttiHannoApprovato = true;
|
|
_ioHoApprovato = true;
|
|
_statusText = "DATI APPROVATI!\nPDF creato, procedi con il salvataggio o l'invio";
|
|
_statusColor = Colors.green.shade800;
|
|
_statusIcon = Icons.check_circle;
|
|
});
|
|
String? id = GlobalData.idSessione ?? GlobalData.idScambioTemporaneo;
|
|
if (id != null) FirebaseFirestore.instance.collection('scambi_cid').doc(id).delete().catchError((_){});
|
|
}
|
|
}
|
|
else if (_ioHoApprovato) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_statusText = "Hai approvato. In attesa dell'altro utente...";
|
|
_statusColor = Colors.amber.shade800;
|
|
_statusIcon = Icons.hourglass_top;
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
Future<void> _eseguiPuliziaFirebase({required bool notificaAltri}) async {
|
|
setState(() {
|
|
_isLoading = true;
|
|
_cancellazioneAvviataDaMe = true;
|
|
});
|
|
|
|
await _roomSubscription?.cancel();
|
|
_roomSubscription = null;
|
|
|
|
Set<String> idsDaCancellare = {};
|
|
if (GlobalData.idScambioTemporaneo != null) idsDaCancellare.add(GlobalData.idScambioTemporaneo!);
|
|
if (GlobalData.idSessione != null) idsDaCancellare.add(GlobalData.idSessione!);
|
|
|
|
for (String id in idsDaCancellare) {
|
|
if (notificaAltri) {
|
|
try {
|
|
await FirebaseFirestore.instance.collection('scambi_cid').doc(id).update({'status': 'retry'})
|
|
.timeout(const Duration(seconds: 2));
|
|
await Future.delayed(const Duration(milliseconds: 300));
|
|
} catch (_) {}
|
|
}
|
|
try {
|
|
await FirebaseFirestore.instance.collection('scambi_cid').doc(id).delete();
|
|
} catch (_) {}
|
|
}
|
|
}
|
|
|
|
Future<void> _tornaIndietroConPulizia() async {
|
|
await _eseguiPuliziaFirebase(notificaAltri: true);
|
|
_resetDatiLocali();
|
|
|
|
if (mounted) {
|
|
setState(() => _isLoading = false);
|
|
if (GlobalData.latoCorrente == 'A') {
|
|
Navigator.pushReplacement(context, MaterialPageRoute(builder: (c) => const Comp1_5Screen()));
|
|
} else {
|
|
Navigator.pushReplacement(context, MaterialPageRoute(builder: (c) => const Comp6_7Screen()));
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _abbandonaScambioEHome() async {
|
|
await _eseguiPuliziaFirebase(notificaAltri: true);
|
|
GlobalData.reset();
|
|
|
|
if (mounted) {
|
|
setState(() => _isLoading = false);
|
|
Navigator.pushAndRemoveUntil(
|
|
context,
|
|
MaterialPageRoute(builder: (c) => const HomeScreen()),
|
|
(route) => false
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<void> _ioApprovo() async {
|
|
String? id = GlobalData.idSessione ?? GlobalData.idScambioTemporaneo;
|
|
if (id != null) {
|
|
try {
|
|
String field = (GlobalData.latoCorrente == 'A') ? 'approved_A' : 'approved_B';
|
|
await FirebaseFirestore.instance.collection('scambi_cid').doc(id).update({field: true});
|
|
} catch (_) {}
|
|
}
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_ioHoApprovato = true;
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> _concludiEHome() async {
|
|
await _eseguiPuliziaFirebase(notificaAltri: false);
|
|
GlobalData.reset();
|
|
if (mounted) {
|
|
Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (c) => const HomeScreen()), (r) => false);
|
|
}
|
|
}
|
|
|
|
void _resetDatiLocali() {
|
|
if (GlobalData.latoCorrente == 'A') GlobalData.resetB(); else GlobalData.resetA();
|
|
GlobalData.idScambioTemporaneo = null;
|
|
GlobalData.idSessione = null;
|
|
}
|
|
|
|
void _gestisciCancellazioneAltrui() {
|
|
_roomSubscription?.cancel();
|
|
_roomSubscription = null;
|
|
|
|
if (mounted) {
|
|
Navigator.of(context).popUntil((route) => route.isFirst || route.settings.name == null);
|
|
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (ctx) => AlertDialog(
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24.0)),
|
|
backgroundColor: Colors.white,
|
|
surfaceTintColor: Colors.transparent,
|
|
icon: Icon(Icons.warning_amber_rounded, size: 60, color: Colors.amber.shade800),
|
|
iconPadding: const EdgeInsets.only(top: 24, bottom: 16),
|
|
title: Text("Attenzione", textAlign: TextAlign.center, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 22, color: Colors.amber.shade900)),
|
|
content: const Padding(
|
|
padding: EdgeInsets.symmetric(vertical: 8.0),
|
|
child: Text(
|
|
"L'altro utente ha deciso di modificare i propri dati o non ha accettato i tuoi.\n\nSarai riportato alla schermata iniziale dove potrai eventualmente apporre modifiche.",
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(fontSize: 16, height: 1.4, color: Colors.black87),
|
|
),
|
|
),
|
|
actionsPadding: const EdgeInsets.fromLTRB(24, 0, 24, 24),
|
|
actions: [
|
|
SizedBox(
|
|
width: double.infinity,
|
|
child: ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.pop(ctx);
|
|
_resetDatiLocali();
|
|
if (GlobalData.latoCorrente == 'A') {
|
|
Navigator.pushReplacement(context, MaterialPageRoute(builder: (c) => const Comp1_5Screen()));
|
|
} else {
|
|
Navigator.pushReplacement(context, MaterialPageRoute(builder: (c) => const Comp6_7Screen()));
|
|
}
|
|
},
|
|
style: ElevatedButton.styleFrom(backgroundColor: Colors.amber.shade800, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), elevation: 0),
|
|
child: const Text("HO CAPITO", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16))
|
|
),
|
|
)
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<void> _generaDocumenti() async {
|
|
if (!mounted) return;
|
|
setState(() => _isLoading = true);
|
|
try {
|
|
final List<int> pdfBytes = await PdfEngine.generaDocumentoCai();
|
|
if (pdfBytes.isEmpty) throw Exception("PDF vuoto");
|
|
|
|
final appDocDir = await getApplicationDocumentsDirectory();
|
|
final file = File('${appDocDir.path}/CID_${DateTime.now().millisecondsSinceEpoch}.pdf');
|
|
|
|
await file.writeAsBytes(pdfBytes, flush: true);
|
|
|
|
Uint8List? anteprima;
|
|
await for (final page in Printing.raster(Uint8List.fromList(pdfBytes), pages: [0], dpi: 150)) {
|
|
anteprima = await page.toPng(); break;
|
|
}
|
|
if (mounted) {
|
|
setState(() { _filePdfReale = file; _immagineAnteprima = anteprima; _isLoading = false; });
|
|
}
|
|
} catch (e) {
|
|
if (mounted) setState(() => _isLoading = false);
|
|
}
|
|
}
|
|
|
|
Future<void> _vaiAScambioDati() async {
|
|
await Navigator.push(context, MaterialPageRoute(builder: (context) => const ScambioDatiScreen()));
|
|
_verificaStatoPostScambio();
|
|
}
|
|
|
|
void _apriAnteprimaSchermoIntero() {
|
|
if (!_scambioEffettuato || !_datiPresenti || _immagineAnteprima == null || _filePdfReale == null) {
|
|
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Dati non pronti!")));
|
|
return;
|
|
}
|
|
Navigator.push(context, MaterialPageRoute(builder: (context) => ImageViewerScreen(
|
|
imageBytes: _immagineAnteprima!,
|
|
pdfFile: _filePdfReale!,
|
|
isAlreadyApproved: _ioHoApprovato,
|
|
onConfirmCorrection: _tornaIndietroConPulizia,
|
|
onConfirmApproval: _ioApprovo
|
|
)));
|
|
}
|
|
|
|
Future<void> _inviaMailConAllegato(BuildContext context) async {
|
|
if (_filePdfReale == null) return;
|
|
|
|
try {
|
|
bool isA = GlobalData.latoCorrente == 'A';
|
|
String polizzaChiScrive = (isA ? GlobalData.Numero_Polizza_A : GlobalData.Numero_Polizza_B).trim();
|
|
String targaChiScrive = (isA ? GlobalData.Targa_A : GlobalData.Targa_B).trim();
|
|
String firmaChiScrive = "${isA ? GlobalData.Nome_contraente_A : GlobalData.Nome_contraente_B} ${isA ? GlobalData.Cognome_contraente_A : GlobalData.Cognome_contraente_B}";
|
|
String contattoChiScrive = (isA ? GlobalData.N_telefono_mail_contraente_A : GlobalData.N_telefono_mail_contraente_B).trim();
|
|
|
|
String compagniaUtente = (isA ? GlobalData.Denominazione_A : GlobalData.Denominazione_B).trim().toUpperCase();
|
|
String emailDestinatario = "";
|
|
|
|
if (GlobalData.assicurazioni.containsKey(compagniaUtente)) {
|
|
emailDestinatario = GlobalData.assicurazioni[compagniaUtente]!;
|
|
} else {
|
|
for (var key in GlobalData.assicurazioni.keys) {
|
|
if (key.isNotEmpty && (compagniaUtente.contains(key) || key.contains(compagniaUtente))) {
|
|
emailDestinatario = GlobalData.assicurazioni[key]!;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
List<String> listaCC = [];
|
|
if (contattoChiScrive.contains("@")) listaCC.add(contattoChiScrive);
|
|
|
|
String oggetto = "DENUNCIA SINISTRO - Polizza n. $polizzaChiScrive - Targa $targaChiScrive";
|
|
String corpo = "Spett.le Compagnia,\n\n"
|
|
"Con la presente inoltro in allegato il modulo CAI relativo al sinistro avvenuto in data ${GlobalData.data_incidente} alle ore ${GlobalData.ora} nel comune di ${GlobalData.luogo}.\n\n"
|
|
"Rimaniamo in attesa dell'apertura del fascicolo.\n\n"
|
|
"Cordiali saluti,\n$firmaChiScrive\nContatto: $contattoChiScrive";
|
|
|
|
final Email email = Email(
|
|
subject: oggetto,
|
|
body: corpo,
|
|
recipients: emailDestinatario.isNotEmpty ? [emailDestinatario] : [],
|
|
cc: listaCC,
|
|
attachmentPaths: [_filePdfReale!.path],
|
|
isHTML: false,
|
|
);
|
|
|
|
await FlutterEmailSender.send(email);
|
|
|
|
} catch (e) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Nessuna app Mail predefinita trovata. Apro la condivisione..."), duration: Duration(seconds: 3), backgroundColor: Colors.orange));
|
|
_apriCondivisione(context);
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _apriCondivisione(BuildContext context) async {
|
|
if (_filePdfReale == null) return;
|
|
final box = context.findRenderObject() as RenderBox?;
|
|
await Share.shareXFiles(
|
|
[XFile(_filePdfReale!.path, mimeType: 'application/pdf')],
|
|
subject: 'Modulo CAI',
|
|
text: 'Ecco il modulo CAI compilato.',
|
|
sharePositionOrigin: box != null ? (box.localToGlobal(Offset.zero) & box.size) : null,
|
|
);
|
|
}
|
|
|
|
Future<void> _salvaPdfLocale(BuildContext context) async {
|
|
await _apriCondivisione(context);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_roomSubscription?.cancel();
|
|
WidgetsBinding.instance.removeObserver(this);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
bool pdfPronto = !_isLoading && _filePdfReale != null && _immagineAnteprima != null;
|
|
bool abilitaAnteprima = _scambioEffettuato && _datiPresenti && pdfPronto;
|
|
bool abilitaFinali = _tuttiHannoApprovato && pdfPronto;
|
|
|
|
String testoAnteprima = !_scambioEffettuato ? "2. ANTEPRIMA (Prima fai Scambio)" :
|
|
(_ioHoApprovato ? "ANTEPRIMA (IN ATTESA...)" : "2. APRI ANTEPRIMA E APPROVA");
|
|
if (_tuttiHannoApprovato) testoAnteprima = "ANTEPRIMA (COMPLETATA)";
|
|
|
|
return Container(
|
|
width: double.infinity,
|
|
height: double.infinity,
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFF0F4F8),
|
|
image: DecorationImage(
|
|
image: const AssetImage('assets/sfondo_mappa.jpg'),
|
|
fit: BoxFit.cover,
|
|
colorFilter: ColorFilter.mode(
|
|
const Color(0xFFF0F4F8).withOpacity(0.6),
|
|
BlendMode.lighten,
|
|
),
|
|
),
|
|
),
|
|
child: PopScope(
|
|
canPop: false,
|
|
onPopInvoked: (didPop) async {
|
|
if (didPop) return;
|
|
if (_ioHoApprovato) _concludiEHome(); else _tornaIndietroConPulizia();
|
|
},
|
|
child: Scaffold(
|
|
backgroundColor: Colors.transparent,
|
|
extendBodyBehindAppBar: true,
|
|
appBar: AppBar(
|
|
title: const Text("Invio e Salvataggio", style: TextStyle(fontWeight: FontWeight.w800, fontSize: 20)),
|
|
centerTitle: true,
|
|
backgroundColor: Colors.blue.shade900.withOpacity(0.95),
|
|
foregroundColor: Colors.white,
|
|
elevation: 10,
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.arrow_back),
|
|
onPressed: _ioHoApprovato ? _concludiEHome : _tornaIndietroConPulizia
|
|
),
|
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(bottom: Radius.circular(20))),
|
|
),
|
|
body: Stack(children: [
|
|
SafeArea(
|
|
child: SingleChildScrollView(
|
|
padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 20),
|
|
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
|
|
|
|
_buildStatusCard(),
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
// VA DIRETTO ALLA PAGINA DI SCAMBIO
|
|
_btn("1. SCAMBIO DATI", Icons.sync_alt, Colors.orange.shade800, onTap: _vaiAScambioDati, disabled: _ioHoApprovato),
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
_btn(testoAnteprima, Icons.visibility, _statusColor, onTap: abilitaAnteprima ? _apriAnteprimaSchermoIntero : null, disabled: !abilitaAnteprima),
|
|
|
|
const SizedBox(height: 8),
|
|
Divider(color: Colors.white.withOpacity(0.5), thickness: 1),
|
|
const SizedBox(height: 8),
|
|
|
|
Builder(builder: (ctx) => _btn("SALVA SUL DISPOSITIVO", Icons.save_alt, Colors.green.shade700, onTap: abilitaFinali ? () => _salvaPdfLocale(ctx) : null, disabled: !abilitaFinali)),
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
Builder(builder: (ctx) => _btn("INVIA ALL'ASSICURAZIONE", Icons.send_rounded, Colors.green.shade700, onTap: abilitaFinali ? () => _inviaMailConAllegato(ctx) : null, disabled: !abilitaFinali)),
|
|
|
|
const SizedBox(height: 40),
|
|
|
|
_btn(
|
|
_tuttiHannoApprovato ? "TORNA ALLA HOME" : "CANCELLA TUTTO E ESCI",
|
|
_tuttiHannoApprovato ? Icons.home : Icons.delete_sweep,
|
|
_tuttiHannoApprovato ? Colors.green.shade800 : Colors.red.shade900,
|
|
onTap: _tuttiHannoApprovato ? _concludiEHome : _abbandonaScambioEHome,
|
|
disabled: false
|
|
),
|
|
|
|
const SizedBox(height: 30)
|
|
]))),
|
|
|
|
if (_isLoading)
|
|
Container(color: Colors.black54, child: const Center(child: Column(mainAxisSize: MainAxisSize.min, children: [CircularProgressIndicator(color: Colors.white), SizedBox(height: 20), Text("Elaborazione in corso...", style: TextStyle(color: Colors.white))]))),
|
|
]),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _btn(String label, IconData icon, Color color, {VoidCallback? onTap, bool disabled = false}) {
|
|
bool on = onTap != null && !disabled;
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: on ? [BoxShadow(color: Colors.black.withOpacity(0.3), offset: const Offset(0, 4), blurRadius: 5)] : [],
|
|
),
|
|
child: ElevatedButton(
|
|
onPressed: on ? onTap : null,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: on ? color : Colors.grey,
|
|
foregroundColor: Colors.white,
|
|
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 20),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
|
),
|
|
child: Row(children: [
|
|
Icon(icon, size: 28), const SizedBox(width: 20),
|
|
Expanded(child: Text(label, textAlign: TextAlign.center, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16))),
|
|
const Icon(Icons.lock, size: 20, color: Colors.transparent)
|
|
])
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildStatusCard() {
|
|
return Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(color: Colors.white.withOpacity(0.9), borderRadius: BorderRadius.circular(16), border: Border.all(color: _statusColor, width: 2), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.1), blurRadius: 6, offset: const Offset(0, 3))]),
|
|
child: Row(children: [
|
|
Icon(_statusIcon, color: _statusColor, size: 36), const SizedBox(width: 15),
|
|
Expanded(child: Text(_statusText, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: _statusColor)))
|
|
])
|
|
);
|
|
}
|
|
}
|
|
|
|
class ImageViewerScreen extends StatelessWidget {
|
|
final Uint8List imageBytes;
|
|
final File pdfFile;
|
|
final bool isAlreadyApproved;
|
|
final Function onConfirmCorrection;
|
|
final Function onConfirmApproval;
|
|
|
|
const ImageViewerScreen({super.key, required this.imageBytes, required this.pdfFile, required this.isAlreadyApproved, required this.onConfirmCorrection, required this.onConfirmApproval});
|
|
|
|
Future<void> _askCorrection(BuildContext context) async {
|
|
String titolo = isAlreadyApproved ? "Chiudere?" : "Richiedere correzione?";
|
|
String testo = isAlreadyApproved
|
|
? "Hai già approvato. Uscendo tornerai alla schermata precedente in attesa dell'altro utente."
|
|
: "Questo annullerà lo scambio per entrambi e vi riporterà alla modifica.";
|
|
String tasto = isAlreadyApproved ? "CHIUDI" : "CORREGGI";
|
|
|
|
bool? conf = await showDialog(context: context, builder: (c) => AlertDialog(
|
|
title: Text(titolo),
|
|
content: Text(testo),
|
|
actions: [
|
|
TextButton(onPressed: () => Navigator.pop(c, false), child: const Text("ANNULLA")),
|
|
ElevatedButton(onPressed: () => Navigator.pop(c, true), child: Text(tasto))
|
|
]
|
|
));
|
|
if (conf == true) {
|
|
Navigator.pop(context);
|
|
if (!isAlreadyApproved) onConfirmCorrection();
|
|
}
|
|
}
|
|
|
|
Future<void> _askApproval(BuildContext context) async { Navigator.pop(context); onConfirmApproval(); }
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: Colors.white,
|
|
appBar: AppBar(title: const Text("Verifica Dati"), backgroundColor: Colors.black, foregroundColor: Colors.white),
|
|
body: Column(children: [
|
|
Expanded(child: InteractiveViewer(minScale: 0.5, maxScale: 4.0, child: Center(child: Container(color: Colors.white, child: Image.memory(imageBytes, fit: BoxFit.contain))))),
|
|
Container(
|
|
padding: const EdgeInsets.all(16.0),
|
|
decoration: BoxDecoration(color: Colors.white, boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 10, offset: const Offset(0, -2))]),
|
|
child: SafeArea(
|
|
child: Row(children: [
|
|
Expanded(
|
|
child: ElevatedButton.icon(
|
|
onPressed: () => _askCorrection(context),
|
|
icon: Icon(isAlreadyApproved ? Icons.arrow_back : Icons.edit),
|
|
label: Text(isAlreadyApproved ? "INDIETRO" : "CORREGGI"),
|
|
style: ElevatedButton.styleFrom(backgroundColor: Colors.orange.shade800, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))),
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
child: ElevatedButton.icon(
|
|
onPressed: isAlreadyApproved ? null : () => _askApproval(context),
|
|
icon: isAlreadyApproved ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white)) : const Icon(Icons.check_circle),
|
|
label: Text(isAlreadyApproved ? "IN ATTESA..." : "APPROVA"),
|
|
style: ElevatedButton.styleFrom(backgroundColor: isAlreadyApproved ? Colors.grey : Colors.green.shade700, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))),
|
|
),
|
|
),
|
|
]),
|
|
),
|
|
)
|
|
]),
|
|
);
|
|
}
|
|
} |