import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'dart:io'; import 'dart:convert'; class PrinterPage extends StatefulWidget { const PrinterPage({super.key}); @override _PrinterPageState createState() => _PrinterPageState(); } class _PrinterPageState extends State { static const String printerIP = '192.168.123.251'; static const int printerPort = 9100; bool _isPrinting = false; bool _isConnected = false; bool _isTestingConnection = false; String _statusMessage = 'Non testé'; @override void initState() { super.initState(); _testConnection(); } Future _testConnection() async { setState(() { _isTestingConnection = true; _statusMessage = 'Test en cours...'; }); try { final socket = await Socket.connect( printerIP, printerPort, ).timeout(const Duration(seconds: 5)); await socket.close(); setState(() { _isConnected = true; _statusMessage = 'Connectée'; _isTestingConnection = false; }); } catch (e) { setState(() { _isConnected = false; _statusMessage = 'Erreur: $e'; _isTestingConnection = false; }); } } Future _printReceipt() async { try { final socket = await Socket.connect(printerIP, printerPort); final List bytes = []; // Initialisation ESC/POS bytes.addAll([0x1B, 0x40]); // ESC @ // Titre centré et agrandi bytes.addAll([0x1B, 0x61, 0x01]); // Centre bytes.addAll([0x1D, 0x21, 0x11]); // Taille double bytes.addAll(utf8.encode('REÇU DE VENTE')); bytes.addAll([0x0A, 0x0A]); // Retour taille normale bytes.addAll([0x1D, 0x21, 0x00]); bytes.addAll([0x1B, 0x61, 0x00]); // Alignement gauche // Informations transaction final now = DateTime.now(); final dateStr = '${now.day.toString().padLeft(2, '0')}/${now.month.toString().padLeft(2, '0')}/${now.year} ${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}'; bytes.addAll(utf8.encode('Date: $dateStr\n')); bytes.addAll(utf8.encode('Caissier: Admin\n')); bytes.addAll(utf8.encode('--------------------------------\n')); // En-têtes colonnes bytes.addAll(utf8.encode('Article Qte Prix\n')); bytes.addAll(utf8.encode('--------------------------------\n')); // Articles bytes.addAll(utf8.encode('Produit 1 2 25.00€\n')); bytes.addAll(utf8.encode('Produit 2 1 15.50€\n')); bytes.addAll(utf8.encode('--------------------------------\n')); // Total bytes.addAll([0x1B, 0x45, 0x01]); // Gras ON bytes.addAll(utf8.encode('TOTAL 40.50€\n')); bytes.addAll([0x1B, 0x45, 0x00]); // Gras OFF bytes.addAll([0x0A, 0x0A]); // Message de remerciement centré bytes.addAll([0x1B, 0x61, 0x01]); // Centre bytes.addAll(utf8.encode('Merci de votre visite !\n')); bytes.addAll([0x0A, 0x0A]); // Coupe papier bytes.addAll([0x1D, 0x56, 0x00]); // GS V 0 // Envoi des données socket.add(bytes); await socket.flush(); await socket.close(); return true; } catch (e) { if (kDebugMode) { print('Erreur impression: $e'); } return false; } } Future _handlePrint() async { setState(() => _isPrinting = true); final success = await _printReceipt(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( success ? 'Impression réussie!' : 'Échec de l\'impression', ), backgroundColor: success ? Colors.green : Colors.red, duration: const Duration(seconds: 3), ), ); setState(() => _isPrinting = false); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Imprimante POS'), backgroundColor: Colors.blue, foregroundColor: Colors.white, ), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Status de connexion Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Status de l\'imprimante', style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 10), Row( children: [ if (_isTestingConnection) const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) else Icon( _isConnected ? Icons.wifi : Icons.wifi_off, color: _isConnected ? Colors.green : Colors.red, size: 20, ), const SizedBox(width: 10), Expanded( child: Text( 'IP: $printerIP:$printerPort\nStatus: $_statusMessage', style: const TextStyle(fontSize: 14), ), ), ], ), const SizedBox(height: 10), ElevatedButton.icon( onPressed: _isTestingConnection ? null : _testConnection, icon: const Icon(Icons.refresh), label: const Text('Tester la connexion'), ), ], ), ), ), const SizedBox(height: 20), // Aperçu du reçu Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Aperçu du reçu', style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 10), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( border: Border.all(color: Colors.grey), borderRadius: BorderRadius.circular(4), color: Colors.grey[50], ), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ const Text( 'REÇU DE VENTE', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 10), Align( alignment: Alignment.centerLeft, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Date: ${DateTime.now().toString().substring(0, 16)}', ), const Text('Caissier: Admin'), const Text('--------------------------------'), const Text('Article Qte Prix'), const Text('--------------------------------'), const Text('Produit 1 2 25.00€'), const Text('Produit 2 1 15.50€'), const Text('--------------------------------'), const Text( 'TOTAL 40.50€', style: TextStyle(fontWeight: FontWeight.bold), ), ], ), ), const SizedBox(height: 10), const Text('Merci de votre visite !'), ], ), ), ], ), ), ), const SizedBox(height: 20), // Bouton d'impression ElevatedButton.icon( onPressed: (_isConnected && !_isPrinting && !_isTestingConnection) ? _handlePrint : null, icon: _isPrinting ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( Colors.white, ), ), ) : const Icon(Icons.print), label: Text(_isPrinting ? 'Impression...' : 'Imprimer le reçu'), style: ElevatedButton.styleFrom( backgroundColor: Colors.green, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 15), textStyle: const TextStyle(fontSize: 16), ), ), if (!_isConnected && !_isTestingConnection) const Padding( padding: EdgeInsets.only(top: 10), child: Text( 'Vérifiez que l\'imprimante est allumée et connectée au réseau', style: TextStyle(color: Colors.orange, fontSize: 12), textAlign: TextAlign.center, ), ), ], ), ), ); } }