210 lines
No EOL
8.1 KiB
Dart
210 lines
No EOL
8.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'global_data.dart';
|
|
import 'comp_16.dart';
|
|
|
|
class Comp15Screen extends StatefulWidget {
|
|
const Comp15Screen({super.key});
|
|
@override
|
|
State<Comp15Screen> createState() => _Comp15ScreenState();
|
|
}
|
|
|
|
class _Comp15ScreenState extends State<Comp15Screen> {
|
|
late List<Offset?> _puntiFirma;
|
|
late bool isB;
|
|
bool _isNavigating = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
isB = GlobalData.latoCorrente == 'B';
|
|
_puntiFirma = isB
|
|
? List<Offset?>.from(GlobalData.puntiFirmaB)
|
|
: List<Offset?>.from(GlobalData.puntiFirmaA);
|
|
|
|
// Appena entro, ruoto in orizzontale
|
|
SystemChrome.setPreferredOrientations([
|
|
DeviceOrientation.landscapeLeft,
|
|
DeviceOrientation.landscapeRight,
|
|
]);
|
|
}
|
|
|
|
// --- PUNTO CHIAVE: LA PULIZIA AUTOMATICA ---
|
|
@override
|
|
void dispose() {
|
|
// Quando questa pagina viene distrutta (in qualsiasi modo),
|
|
// FORZO immediatamente il ritorno al verticale.
|
|
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
|
super.dispose();
|
|
}
|
|
|
|
Future<void> _tornaIndietro() async {
|
|
_salvaInMemoria();
|
|
if (mounted) Navigator.pop(context);
|
|
}
|
|
|
|
void _salvaInMemoria() {
|
|
if (isB) {
|
|
GlobalData.puntiFirmaB = List.from(_puntiFirma);
|
|
} else {
|
|
GlobalData.puntiFirmaA = List.from(_puntiFirma);
|
|
}
|
|
}
|
|
|
|
Future<void> _confermaEProsegui() async {
|
|
_salvaInMemoria();
|
|
|
|
if (_puntiFirma.isEmpty) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text("La firma è obbligatoria!"), backgroundColor: Colors.red),
|
|
);
|
|
return;
|
|
}
|
|
|
|
setState(() => _isNavigating = true);
|
|
|
|
if (mounted) {
|
|
// Prima di andare alla 16, forzo GIA' il verticale qui.
|
|
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
|
|
|
// Piccola pausa per dare tempo all'animazione di rotazione
|
|
await Future.delayed(const Duration(milliseconds: 100));
|
|
|
|
if (!mounted) return;
|
|
|
|
// Navigo verso la 16. Uso pushReplacement per distruggere la 15 (e chiamare dispose)
|
|
// oppure push normale, ma avendo già forzato il portrait sopra siamo sicuri.
|
|
await Navigator.push(
|
|
context,
|
|
MaterialPageRoute(builder: (c) => const Comp16Screen())
|
|
);
|
|
|
|
// --- AGGIUNTA FONDAMENTALE ---
|
|
// Aspettiamo un attimo. Se stiamo facendo "Cancella tutto",
|
|
// in questo lasso di tempo la pagina verrà smontata (mounted diventerà false)
|
|
// e il codice sotto NON verrà eseguito, evitando la trottola.
|
|
await Future.delayed(const Duration(milliseconds: 300));
|
|
// -----------------------------
|
|
|
|
if (mounted && ModalRoute.of(context)?.isCurrent == true) {
|
|
await SystemChrome.setPreferredOrientations([
|
|
DeviceOrientation.landscapeLeft,
|
|
DeviceOrientation.landscapeRight,
|
|
]);
|
|
setState(() => _isNavigating = false);
|
|
}
|
|
// ---------------------------
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// Ribadisco orizzontale nel build per sicurezza
|
|
// SystemChrome.setPreferredOrientations([
|
|
// DeviceOrientation.landscapeLeft,
|
|
// DeviceOrientation.landscapeRight,
|
|
// ]);
|
|
|
|
return PopScope(
|
|
canPop: false,
|
|
onPopInvokedWithResult: (didPop, result) async {
|
|
if (!didPop) await _tornaIndietro();
|
|
},
|
|
child: Scaffold(
|
|
backgroundColor: Colors.white,
|
|
resizeToAvoidBottomInset: false,
|
|
body: OrientationBuilder(
|
|
builder: (context, orientation) {
|
|
double shortestSide = MediaQuery.of(context).size.shortestSide;
|
|
bool isTablet = shortestSide > 600;
|
|
|
|
// Fix per iPad che potrebbe rimanere verticale
|
|
if (orientation == Orientation.portrait) {
|
|
return Center(
|
|
child: RotatedBox(
|
|
quarterTurns: 1,
|
|
child: SizedBox(
|
|
width: MediaQuery.of(context).size.height,
|
|
height: MediaQuery.of(context).size.width,
|
|
child: SafeArea(child: _buildBody(context, isTablet)),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
return SafeArea(child: _buildBody(context, isTablet));
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildBody(BuildContext context, bool isTablet) {
|
|
Color mainCol = isB ? Colors.amber.shade700 : Colors.blue.shade900;
|
|
double shortestSide = MediaQuery.of(context).size.shortestSide;
|
|
double spessoreFirma = (shortestSide * 0.01).clamp(3.0, 8.0);
|
|
double verticalPadding = isTablet ? MediaQuery.of(context).size.height * 0.12 : 2.0;
|
|
double horizontalPadding = isTablet ? 80.0 : 5.0;
|
|
|
|
return _isNavigating
|
|
? const Center(child: CircularProgressIndicator())
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Container(
|
|
height: 50, color: mainCol, padding: const EdgeInsets.symmetric(horizontal: 10),
|
|
child: Row(children: [
|
|
IconButton(icon: const Icon(Icons.arrow_back, color: Colors.white), onPressed: _tornaIndietro),
|
|
Expanded(child: Text("15. Firma (${GlobalData.latoCorrente})", textAlign: TextAlign.center, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18))),
|
|
const SizedBox(width: 48),
|
|
]),
|
|
),
|
|
Expanded(
|
|
child: Padding(
|
|
padding: EdgeInsets.symmetric(vertical: verticalPadding, horizontal: horizontalPadding),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
if (isTablet) ...[const Text("Firma nello spazio sottostante:", style: TextStyle(fontSize: 16)), const SizedBox(height: 8)],
|
|
Expanded(
|
|
child: Container(
|
|
width: double.infinity,
|
|
decoration: BoxDecoration(border: Border.all(color: Colors.grey, width: 2), borderRadius: BorderRadius.circular(10), color: Colors.grey.shade50),
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(8),
|
|
child: GestureDetector(
|
|
onPanUpdate: (d) => setState(() => _puntiFirma.add(d.localPosition)),
|
|
onPanEnd: (d) => setState(() => _puntiFirma.add(null)),
|
|
child: RepaintBoundary(child: CustomPaint(painter: FirmaPainter(_puntiFirma, spessoreFirma), size: Size.infinite)),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: EdgeInsets.fromLTRB(20, 5, 20, isTablet ? 15 : 5),
|
|
child: Row(children: [
|
|
Expanded(flex: 1, child: SizedBox(height: 45, child: OutlinedButton.icon(onPressed: () => setState(() => _puntiFirma.clear()), icon: const Icon(Icons.delete, color: Colors.red), label: const Text("CANCELLA", style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold)), style: OutlinedButton.styleFrom(side: const BorderSide(color: Colors.red))))),
|
|
const SizedBox(width: 20),
|
|
Expanded(flex: 2, child: SizedBox(height: 45, child: ElevatedButton.icon(onPressed: _confermaEProsegui, style: ElevatedButton.styleFrom(backgroundColor: Colors.green[700], foregroundColor: Colors.white, elevation: 5), icon: const Icon(Icons.check_circle_outline), label: const Text("CONFERMA FIRMA", style: TextStyle(fontWeight: FontWeight.bold))))),
|
|
]),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class FirmaPainter extends CustomPainter {
|
|
final List<Offset?> punti;
|
|
final double spessore;
|
|
FirmaPainter(this.punti, this.spessore);
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
Paint p = Paint()..color = Colors.black..strokeWidth = spessore..strokeCap = StrokeCap.round..strokeJoin = StrokeJoin.round..style = PaintingStyle.stroke..isAntiAlias = true;
|
|
for (int i = 0; i < punti.length - 1; i++) { if (punti[i] != null && punti[i + 1] != null) canvas.drawLine(punti[i]!, punti[i + 1]!, p); }
|
|
}
|
|
@override
|
|
bool shouldRepaint(FirmaPainter old) => true;
|
|
} |