Browse Source

commit 09082025

master
andrymodeste 4 months ago
parent
commit
3c8dfbcac7
  1. 5
      lib/layouts/main_layout.dart
  2. 6
      lib/main.dart
  3. 52
      lib/models/printerModel.dart
  4. 4
      lib/pages/cart_page.dart
  5. 4
      lib/pages/commandes_screen.dart
  6. 619
      lib/pages/printer_page.dart
  7. 829
      lib/services/pdf_impression_commande.dart
  8. 1
      lib/services/pdf_service.dart
  9. 47
      lib/services/restaurant_api_service.dart
  10. 47
      lib/widgets/bottom_navigation.dart
  11. 4
      linux/flutter/generated_plugin_registrant.cc
  12. 1
      linux/flutter/generated_plugins.cmake
  13. 74
      pubspec.lock
  14. 3
      windows/flutter/generated_plugin_registrant.cc
  15. 1
      windows/flutter/generated_plugins.cmake

5
lib/layouts/main_layout.dart

@ -40,6 +40,8 @@ class _MainLayoutState extends State<MainLayout> {
return 6; return 6;
case '/information': case '/information':
return 7; return 7;
case '/Setting':
return 8;
default: default:
return 0; return 0;
} }
@ -76,6 +78,9 @@ class _MainLayoutState extends State<MainLayout> {
case 7: case 7:
route = '/information'; route = '/information';
break; break;
case 8:
route = '/Setting';
break;
default: default:
route = '/tables'; route = '/tables';
} }

6
lib/main.dart

@ -7,6 +7,7 @@ import 'pages/login_screen.dart';
import 'pages/menus_screen.dart'; import 'pages/menus_screen.dart';
import 'pages/historique_commande.dart'; import 'pages/historique_commande.dart';
import 'pages/information.dart'; import 'pages/information.dart';
import 'pages/printer_page.dart';
import 'pages/encaissement_screen.dart'; // NOUVEAU import 'pages/encaissement_screen.dart'; // NOUVEAU
void main() { void main() {
@ -64,6 +65,11 @@ class MyApp extends StatelessWidget {
currentRoute: '/information', currentRoute: '/information',
child: PrintTemplateManagementScreen(), child: PrintTemplateManagementScreen(),
), ),
'/Setting':
(context) => MainLayout(
currentRoute: '/Setting',
child: PrinterPage(),
),
}, },
); );
} }

52
lib/models/printerModel.dart

@ -0,0 +1,52 @@
// models/printer_settings.dart
class PrinterSettings {
final String? ipAddress;
final int? port;
final String? name;
final String? type;
final int? id;
final DateTime? createdAt;
final DateTime? updatedAt;
PrinterSettings({
this.ipAddress,
this.port,
this.name,
this.type,
this.id,
this.createdAt,
this.updatedAt,
});
factory PrinterSettings.fromJson(Map<String, dynamic> json) {
return PrinterSettings(
ipAddress: json['ip_address'],
port: json['port'] ?? 9100,
name: json['name'],
type: json['type'],
id: json['id'],
createdAt: json['created_at'] != null ? DateTime.parse(json['created_at']) : null,
updatedAt: json['updated_at'] != null ? DateTime.parse(json['updated_at']) : null,
);
}
Map<String, dynamic> toJson() {
return {
'ip_address': ipAddress,
'port': port,
'name': name,
'type': type,
'id': id,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
};
}
// Méthode pour créer un objet de test de connexion
Map<String, dynamic> toTestJson() {
return {
'ip_address': ipAddress,
'port': port,
};
}
}

4
lib/pages/cart_page.dart

@ -255,7 +255,9 @@ class _CartPageState extends State<CartPage> {
actions: [ actions: [
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
printOrderPDF(order); printOrderWithFeedback(order, (message, isSuccess) {
print('$message - Success: $isSuccess');
});
Navigator.of(context).pushAndRemoveUntil( Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute( MaterialPageRoute(
builder: builder:

4
lib/pages/commandes_screen.dart

@ -615,7 +615,9 @@ class OrderCard extends StatelessWidget {
// Bouton imprimer // Bouton imprimer
ElevatedButton.icon( ElevatedButton.icon(
onPressed: () { onPressed: () {
printOrderPDF(order); printOrderWithFeedback(order, (message, isSuccess) {
print('$message - Success: $isSuccess');
});
}, },
icon: const Icon(Icons.print, color: Colors.white, size: 16), icon: const Icon(Icons.print, color: Colors.white, size: 16),
label: const Text( label: const Text(

619
lib/pages/printer_page.dart

@ -2,6 +2,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'dart:io'; import 'dart:io';
import 'dart:convert'; import 'dart:convert';
// Ajoutez l'import de votre service API et du modèle
import '../services/restaurant_api_service.dart';
class PrinterPage extends StatefulWidget { class PrinterPage extends StatefulWidget {
const PrinterPage({super.key}); const PrinterPage({super.key});
@ -11,18 +13,55 @@ class PrinterPage extends StatefulWidget {
} }
class _PrinterPageState extends State<PrinterPage> { class _PrinterPageState extends State<PrinterPage> {
static const String printerIP = '192.168.123.251'; // Remplacer les constantes par des variables
static const int printerPort = 9100; String printerIP = '192.168.123.251'; // Valeur par défaut
int printerPort = 9100; // Valeur par défaut
bool _isPrinting = false; bool _isPrinting = false;
bool _isConnected = false; bool _isConnected = false;
bool _isTestingConnection = false; bool _isTestingConnection = false;
bool _isLoadingSettings = false;
String _statusMessage = 'Non testé'; String _statusMessage = 'Non testé';
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_testConnection(); _loadPrinterSettings();
}
Future<void> _loadPrinterSettings() async {
setState(() {
_isLoadingSettings = true;
_statusMessage = 'Chargement des paramètres...';
});
try {
final printerSettings = await RestaurantApiService.getPrinterSettings();
print(printerSettings.ipAddress);
setState(() {
// Utilisation des propriétés du modèle PrinterSettings
printerIP = printerSettings.ipAddress ?? '192.168.123.251';
printerPort = printerSettings.port ?? 9100;
_isLoadingSettings = false;
_statusMessage = 'Paramètres chargés: ${printerSettings.name ?? "Sans nom"}';
});
// Test automatique de la connexion après chargement des paramètres
_testConnection();
} catch (e) {
setState(() {
_isLoadingSettings = false;
_statusMessage = 'Erreur chargement: $e';
});
if (kDebugMode) {
print('Erreur lors du chargement des paramètres d\'imprimante: $e');
}
// En cas d'erreur, on teste quand même avec les valeurs par défaut
_testConnection();
}
} }
Future<void> _testConnection() async { Future<void> _testConnection() async {
@ -135,6 +174,103 @@ class _PrinterPageState extends State<PrinterPage> {
setState(() => _isPrinting = false); setState(() => _isPrinting = false);
} }
void _showEditPrinterDialog() {
final TextEditingController ipController = TextEditingController(text: printerIP);
final TextEditingController portController = TextEditingController(text: printerPort.toString());
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Modifier les paramètres d\'imprimante'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: ipController,
decoration: const InputDecoration(
labelText: 'Adresse IP',
hintText: '192.168.1.100',
prefixIcon: Icon(Icons.computer),
),
keyboardType: TextInputType.numberWithOptions(decimal: true),
),
const SizedBox(height: 16),
TextField(
controller: portController,
decoration: const InputDecoration(
labelText: 'Port',
hintText: '9100',
prefixIcon: Icon(Icons.settings_ethernet),
),
keyboardType: TextInputType.number,
),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () async {
final String newIP = ipController.text.trim();
final int? newPort = int.tryParse(portController.text.trim());
if (newIP.isNotEmpty && newPort != null && newPort > 0 && newPort <= 65535) {
// Appel API pour mise à jour
final apiService = RestaurantApiService();
bool success = await apiService.updatePrinterSettings(
ipAddress: newIP,
port: newPort,
);
if (success) {
setState(() {
printerIP = newIP;
printerPort = newPort;
_isConnected = false;
_statusMessage = 'Paramètres modifiés - Test requis';
});
Navigator.of(context).pop();
_testConnection();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Paramètres mis à jour avec succès'),
backgroundColor: Colors.green,
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Erreur lors de la mise à jour des paramètres'),
backgroundColor: Colors.red,
),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Veuillez entrer une IP et un port valides'),
backgroundColor: Colors.red,
),
);
}
},
child: const Text('Enregistrer'),
),
],
);
},
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -142,164 +278,363 @@ class _PrinterPageState extends State<PrinterPage> {
title: const Text('Imprimante POS'), title: const Text('Imprimante POS'),
backgroundColor: Colors.blue, backgroundColor: Colors.blue,
foregroundColor: Colors.white, foregroundColor: Colors.white,
actions: [
// Bouton pour recharger les paramètres
IconButton(
onPressed: _isLoadingSettings ? null : _loadPrinterSettings,
icon: _isLoadingSettings
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
)
: const Icon(Icons.settings_backup_restore),
tooltip: 'Recharger les paramètres',
),
],
), ),
body: Padding( body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0), child: Padding(
child: Column( padding: const EdgeInsets.all(16.0),
crossAxisAlignment: CrossAxisAlignment.stretch, child: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.stretch,
// Status de connexion children: [
Card( // Status de connexion
child: Padding( Card(
padding: const EdgeInsets.all(16.0), child: Padding(
child: Column( padding: const EdgeInsets.all(16.0),
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
Text( children: [
'Status de l\'imprimante', Text(
style: Theme.of(context).textTheme.titleLarge, 'Status de l\'imprimante',
), style: Theme.of(context).textTheme.titleLarge,
const SizedBox(height: 10), ),
Row( const SizedBox(height: 10),
children: [ Row(
if (_isTestingConnection) children: [
const SizedBox( if (_isTestingConnection || _isLoadingSettings)
width: 20, const SizedBox(
height: 20, width: 20,
child: CircularProgressIndicator(strokeWidth: 2), height: 20,
) child: CircularProgressIndicator(strokeWidth: 2),
else )
Icon( else
_isConnected ? Icons.wifi : Icons.wifi_off, Icon(
color: _isConnected ? Colors.green : Colors.red, _isConnected ? Icons.wifi : Icons.wifi_off,
size: 20, color: _isConnected ? Colors.green : Colors.red,
), size: 20,
const SizedBox(width: 10), ),
Expanded( const SizedBox(width: 10),
child: Text( Expanded(
'IP: $printerIP:$printerPort\nStatus: $_statusMessage', child: Text(
style: const TextStyle(fontSize: 14), '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( const SizedBox(height: 10),
crossAxisAlignment: CrossAxisAlignment.center, Row(
children: [ children: [
const Text( Expanded(
'REÇU DE VENTE', child: ElevatedButton.icon(
style: TextStyle( onPressed: (_isTestingConnection || _isLoadingSettings) ? null : _testConnection,
fontSize: 18, icon: const Icon(Icons.refresh),
fontWeight: FontWeight.bold, label: const Text('Tester la connexion'),
), ),
), ),
const SizedBox(height: 10), const SizedBox(width: 10),
Align( Expanded(
alignment: Alignment.centerLeft, child: ElevatedButton.icon(
child: Column( onPressed: _isLoadingSettings ? null : _loadPrinterSettings,
crossAxisAlignment: CrossAxisAlignment.start, icon: const Icon(Icons.download),
children: [ label: const Text('Recharger paramètres'),
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), const SizedBox(height: 20),
// Bouton d'impression // Aperçu du reçu
ElevatedButton.icon( Card(
onPressed: child: Padding(
(_isConnected && !_isPrinting && !_isTestingConnection) padding: const EdgeInsets.all(16.0),
? _handlePrint child: Column(
: null, crossAxisAlignment: CrossAxisAlignment.start,
icon: children: [
_isPrinting Text(
? const SizedBox( 'Aperçu du reçu',
width: 20, style: Theme.of(context).textTheme.titleLarge,
height: 20, ),
child: CircularProgressIndicator( const SizedBox(height: 10),
strokeWidth: 2, Container(
valueColor: AlwaysStoppedAnimation<Color>( padding: const EdgeInsets.all(12),
Colors.white, decoration: BoxDecoration(
), border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(4),
color: Colors.grey[50],
), ),
) child: Padding(
: const Icon(Icons.print), padding: const EdgeInsets.symmetric(horizontal: 16),
label: Text(_isPrinting ? 'Impression...' : 'Imprimer le reçu'), child: Table(
style: ElevatedButton.styleFrom( columnWidths: const {
backgroundColor: Colors.green, 0: FlexColumnWidth(3), // Colonne gauche large
foregroundColor: Colors.white, 1: FlexColumnWidth(2), // Colonne milieu
padding: const EdgeInsets.symmetric(vertical: 15), 2: FlexColumnWidth(1), // Colonne droite pour le bouton
textStyle: const TextStyle(fontSize: 16), },
border: TableBorder.all(color: Colors.black12),
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
children: [
// Ligne d'en-tête
TableRow(
decoration: BoxDecoration(color: Colors.grey[300]),
children: [
const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'IMPRESSION TEST',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'IP: $printerIP', // Affichage dynamique de l'IP
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
),
),
const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'Actions',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
),
],
),
// Ligne infos facture + IP/Port + Bouton modifier
TableRow(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Date: ${DateTime.now().toString().substring(0, 16)}'),
const Text('Caissier: Admin'),
const SizedBox(height: 8),
const Divider(thickness: 1, color: Colors.black54),
// Petit tableau imbriqué
Table(
defaultColumnWidth: const FixedColumnWidth(80),
border: TableBorder.all(color: Colors.black26),
children: const [
TableRow(
decoration: BoxDecoration(color: Colors.grey),
children: [
Padding(
padding: EdgeInsets.all(4),
child: Text('Article', style: TextStyle(fontWeight: FontWeight.bold)),
),
Padding(
padding: EdgeInsets.all(4),
child: Text('Qte', style: TextStyle(fontWeight: FontWeight.bold)),
),
Padding(
padding: EdgeInsets.all(4),
child: Text('Prix', style: TextStyle(fontWeight: FontWeight.bold)),
),
],
),
TableRow(
children: [
Padding(
padding: EdgeInsets.all(4),
child: Text('Produit 1'),
),
Padding(
padding: EdgeInsets.all(4),
child: Text('2'),
),
Padding(
padding: EdgeInsets.all(4),
child: Text('25.00€'),
),
],
),
TableRow(
children: [
Padding(
padding: EdgeInsets.all(4),
child: Text('Produit 2'),
),
Padding(
padding: EdgeInsets.all(4),
child: Text('1'),
),
Padding(
padding: EdgeInsets.all(4),
child: Text('15.50€'),
),
],
),
],
),
const SizedBox(height: 8),
const Divider(thickness: 1, color: Colors.black54),
const Text(
'TOTAL 40.50€',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
// Colonne milieu avec IP et Port
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text(
'Port: $printerPort',
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: _isConnected ? Colors.green.shade50 : Colors.red.shade50,
borderRadius: BorderRadius.circular(4),
border: Border.all(
color: _isConnected ? Colors.green : Colors.red,
width: 1,
),
),
child: Column(
children: [
Icon(
_isConnected ? Icons.check_circle : Icons.error,
color: _isConnected ? Colors.green : Colors.red,
size: 20,
),
const SizedBox(height: 4),
Text(
_isConnected ? 'Connecté' : 'Déconnecté',
style: TextStyle(
fontSize: 12,
color: _isConnected ? Colors.green.shade700 : Colors.red.shade700,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
),
// Colonne droite avec bouton de modification
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton.icon(
onPressed: _showEditPrinterDialog,
icon: const Icon(Icons.edit, size: 16),
label: const Text(
'Modifier',
style: TextStyle(fontSize: 12),
),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
minimumSize: const Size(0, 32),
),
),
),
],
),
// Ligne vide
const TableRow(
children: [
SizedBox(height: 20),
SizedBox(),
SizedBox(),
],
),
// Ligne finale Merci
const TableRow(
children: [
Padding(
padding: EdgeInsets.all(8),
child: Text('Merci de votre visite !'),
),
SizedBox(),
SizedBox(),
],
),
],
),
)
),
],
),
),
), ),
),
if (!_isConnected && !_isTestingConnection) const SizedBox(height: 20),
const Padding(
padding: EdgeInsets.only(top: 10), // Bouton d'impression
child: Text( ElevatedButton.icon(
'Vérifiez que l\'imprimante est allumée et connectée au réseau', onPressed:
style: TextStyle(color: Colors.orange, fontSize: 12), (_isConnected && !_isPrinting && !_isTestingConnection && !_isLoadingSettings)
textAlign: TextAlign.center, ? _handlePrint
: null,
icon:
_isPrinting
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
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 && !_isLoadingSettings)
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,
),
),
],
),
), ),
), ),
); );
} }
} }

829
lib/services/pdf_impression_commande.dart

@ -1,77 +1,764 @@
import 'package:pdf/widgets.dart' as pw; import 'dart:async';
import 'package:pdf/pdf.dart';
import 'package:printing/printing.dart'; import 'package:flutter/foundation.dart';
import 'package:itrimobe/services/restaurant_api_service.dart';
import 'package:itrimobe/models/printerModel.dart';
import 'dart:io';
import 'dart:convert';
import '../pages/commandes_screen.dart'; import '../pages/commandes_screen.dart';
Future<void> printOrderPDF(Order order) async {
String _formatTime(DateTime dateTime) { // Énumération pour les différents états de connexion
return '${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')} ${dateTime.day.toString().padLeft(2, '0')}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.year}'; enum PrinterConnectionStatus {
notConfigured,
connecting,
connected,
disconnected,
error
}
// Classe pour les résultats de test de connexion
class ConnectionTestResult {
final bool isSuccessful;
final String message;
final PrinterConnectionStatus status;
final Duration responseTime;
const ConnectionTestResult({
required this.isSuccessful,
required this.message,
required this.status,
required this.responseTime,
});
@override
String toString() => 'ConnectionTestResult(success: $isSuccessful, message: "$message", time: ${responseTime.inMilliseconds}ms)';
}
class OrderPrinter {
// Singleton pattern pour garantir une seule instance
static OrderPrinter? _instance;
static OrderPrinter get instance => _instance ??= OrderPrinter._internal();
OrderPrinter._internal();
// Configuration de l'imprimante
PrinterSettings? _settings;
PrinterConnectionStatus _connectionStatus = PrinterConnectionStatus.notConfigured;
// Getters publics
PrinterSettings? get settings => _settings;
PrinterConnectionStatus get connectionStatus => _connectionStatus;
bool get isConfigured => _settings != null &&
_settings!.ipAddress != null &&
_settings!.ipAddress!.isNotEmpty &&
_settings!.port != null;
// Constantes ESC/POS
static const List<int> ESC_CONDENSED_ON = [0x1B, 0x0F];
static const List<int> ESC_CONDENSED_OFF = [0x1B, 0x12];
static const List<int> ESC_INIT = [0x1B, 0x40];
static const List<int> ESC_ALIGN_CENTER = [0x1B, 0x61, 0x01];
static const List<int> ESC_ALIGN_LEFT = [0x1B, 0x61, 0x00];
static const List<int> ESC_ALIGN_RIGHT = [0x1B, 0x61, 0x02];
static const List<int> ESC_DOUBLE_SIZE = [0x1D, 0x21, 0x11];
static const List<int> ESC_NORMAL_SIZE = [0x1D, 0x23, 0x00];
static const List<int> ESC_BOLD_ON = [0x1B, 0x45, 0x01];
static const List<int> ESC_BOLD_OFF = [0x1B, 0x45, 0x00];
static const List<int> ESC_CUT = [0x1D, 0x56, 0x00];
static const List<int> ESC_PAGE_WIDTH_80MM = [0x1D, 0x57, 0x00, 0x02];
static const List<int> ESC_LEFT_MARGIN_0 = [0x1D, 0x4C, 0x015, 0x00];
// Configuration d'impression
static const int maxCharsPerLine = 42;
static const String checkbox = '[ ]';
static const String rightWord = 'cocher';
static const int padding = 1;
/// Initialise la configuration de l'imprimante avec gestion d'erreurs robuste
Future<bool> initialize() async {
try {
_connectionStatus = PrinterConnectionStatus.connecting;
if (kDebugMode) {
print("🔧 Initialisation de la configuration imprimante...");
}
// Récupération des paramètres avec timeout via votre API
var printerSettings = await RestaurantApiService.getPrinterSettings()
.timeout(
const Duration(seconds: 10),
onTimeout: () => throw TimeoutException('Timeout lors de la récupération des paramètres'),
);
// Validation des paramètres
if (printerSettings.ipAddress == null || printerSettings.ipAddress!.isEmpty) {
throw ArgumentError('Adresse IP de l\'imprimante non définie');
}
if (printerSettings.port == null || printerSettings.port! <= 0 || printerSettings.port! > 65535) {
throw ArgumentError('Port de l\'imprimante invalide: ${printerSettings.port}');
}
// Validation du format IP
if (!_isValidIpAddress(printerSettings.ipAddress!)) {
throw ArgumentError('Format d\'adresse IP invalide: ${printerSettings.ipAddress}');
}
_settings=null;
// Configuration réussie
_settings = printerSettings;
_connectionStatus = PrinterConnectionStatus.disconnected;
_settings = PrinterSettings();
_settings = printerSettings;
if (kDebugMode) {
print("✅ Imprimante configurée : ${_settings!.ipAddress}:${_settings!.port}");
if (_settings!.name != null) {
print("📋 Nom: ${_settings!.name}");
}
if (_settings!.type != null) {
print("🏷️ Type: ${_settings!.type}");
}
}
return true;
} on TimeoutException catch (e) {
_connectionStatus = PrinterConnectionStatus.error;
if (kDebugMode) {
print("⏰ Timeout lors de l'initialisation: $e");
}
return false;
} on ArgumentError catch (e) {
_connectionStatus = PrinterConnectionStatus.error;
if (kDebugMode) {
print("❌ Paramètres invalides: $e");
}
return false;
} catch (e) {
_connectionStatus = PrinterConnectionStatus.error;
if (kDebugMode) {
print("💥 Erreur lors de la récupération des paramètres d'imprimante : $e");
}
return false;
}
}
/// Valide le format d'une adresse IP
bool _isValidIpAddress(String ip) {
final ipRegex = RegExp(r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$');
return ipRegex.hasMatch(ip);
} }
final pdf = pw.Document();
/// Test de connexion avancé avec métriques
final pageFormat = PdfPageFormat( Future<ConnectionTestResult> testConnection({
204.1, // 72 mm imprimable Duration timeout = const Duration(seconds: 5),
595.0, // 210 mm int maxRetries = 3,
marginAll: 0, }) async {
); if (!isConfigured) {
return const ConnectionTestResult(
pdf.addPage( isSuccessful: false,
pw.Page( message: 'Imprimante non configurée',
pageFormat: pageFormat, status: PrinterConnectionStatus.notConfigured,
build: (pw.Context context) { responseTime: Duration.zero,
return pw.Padding(
padding: const pw.EdgeInsets.fromLTRB(6, 6, 6, 18),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Divider(),
pw.Text('Commande n° ${order.numeroCommande}', style: pw.TextStyle(fontSize: 10, fontWeight: pw.FontWeight.bold)),
pw.Text('Table: ${order.tablename}', style: pw.TextStyle(fontSize: 8)),
pw.Text('Date: ${_formatTime(order.dateCommande)}', style: pw.TextStyle(fontSize: 8)),
pw.SizedBox(height: 8),
pw.Divider(),
pw.SizedBox(height: 8),
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
children: [
pw.Text('Désignation', style: pw.TextStyle(fontSize: 9, fontWeight: pw.FontWeight.bold)),
pw.Text('Cocher', style: pw.TextStyle(fontSize: 9)),
],
),
pw.SizedBox(height: 8),
pw.Divider(),
...order.items.map(
(item) => pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
children: [
pw.Container(
width: 120,
child: pw.Text(
'${item.quantite} x ${item.nom ?? 'Item'}',
style: pw.TextStyle(fontSize: 8),
),
),
pw.Container(
width: 10,
height: 10,
decoration: pw.BoxDecoration(
border: pw.Border.all(width: 1),
),
),
],
),
),
pw.Divider(),
pw.Spacer(),
pw.SizedBox(height: 10), // marge visible en bas
],
),
); );
}, }
),
); final stopwatch = Stopwatch()..start();
for (int attempt = 1; attempt <= maxRetries; attempt++) {
try {
_connectionStatus = PrinterConnectionStatus.connecting;
if (kDebugMode) {
print("🔍 Test de connexion (tentative $attempt/$maxRetries) vers ${_settings!.ipAddress}:${_settings!.port}");
}
final socket = await Socket.connect(
_settings!.ipAddress!,
_settings!.port!,
).timeout(timeout);
// Test d'écriture simple pour vérifier que l'imprimante répond
socket.add(ESC_INIT);
await socket.flush();
await socket.close();
stopwatch.stop();
_connectionStatus = PrinterConnectionStatus.connected;
final result = ConnectionTestResult(
isSuccessful: true,
message: 'Connexion réussie en ${stopwatch.elapsedMilliseconds}ms',
status: PrinterConnectionStatus.connected,
responseTime: stopwatch.elapsed,
);
if (kDebugMode) {
print("${result.message}");
}
return result;
} on SocketException catch (e) {
if (kDebugMode) {
print("🔌 Erreur de socket (tentative $attempt): $e");
}
if (attempt == maxRetries) {
stopwatch.stop();
_connectionStatus = PrinterConnectionStatus.disconnected;
return ConnectionTestResult(
isSuccessful: false,
message: 'Imprimante inaccessible: ${e.message}',
status: PrinterConnectionStatus.disconnected,
responseTime: stopwatch.elapsed,
);
}
// Attendre avant la prochaine tentative
await Future.delayed(Duration(milliseconds: 500 * attempt));
} on TimeoutException {
if (kDebugMode) {
print("⏰ Timeout de connexion (tentative $attempt)");
}
if (attempt == maxRetries) {
stopwatch.stop();
_connectionStatus = PrinterConnectionStatus.disconnected;
return ConnectionTestResult(
isSuccessful: false,
message: 'Timeout de connexion après ${timeout.inSeconds}s',
status: PrinterConnectionStatus.disconnected,
responseTime: stopwatch.elapsed,
);
}
await Future.delayed(Duration(milliseconds: 500 * attempt));
} catch (e) {
if (kDebugMode) {
print("💥 Erreur inattendue (tentative $attempt): $e");
}
if (attempt == maxRetries) {
stopwatch.stop();
_connectionStatus = PrinterConnectionStatus.error;
return ConnectionTestResult(
isSuccessful: false,
message: 'Erreur de connexion: $e',
status: PrinterConnectionStatus.error,
responseTime: stopwatch.elapsed,
);
}
await Future.delayed(Duration(milliseconds: 500 * attempt));
}
}
// Ce code ne devrait jamais être atteint
stopwatch.stop();
_connectionStatus = PrinterConnectionStatus.error;
return ConnectionTestResult(
isSuccessful: false,
message: 'Échec après $maxRetries tentatives',
status: PrinterConnectionStatus.error,
responseTime: stopwatch.elapsed,
);
}
/// Test de connexion simple (rétrocompatibilité)
Future<bool> testConnectionSimple() async {
final result = await testConnection();
return result.isSuccessful;
}
/// Diagnostic complet de l'imprimante
Future<Map<String, dynamic>> runDiagnostics() async {
final diagnostics = <String, dynamic>{
'timestamp': DateTime.now().toIso8601String(),
'configured': isConfigured,
'connectionStatus': _connectionStatus.toString(),
};
if (_settings != null) {
diagnostics['settings'] = {
'ipAddress': _settings!.ipAddress,
'port': _settings!.port,
'name': _settings!.name,
'type': _settings!.type,
'id': _settings!.id,
'createdAt': _settings!.createdAt?.toIso8601String(),
'updatedAt': _settings!.updatedAt?.toIso8601String(),
};
} else {
diagnostics['settings'] = 'Non configuré';
}
if (isConfigured) {
// Test de résolution DNS (si applicable)
try {
final addresses = await InternetAddress.lookup(_settings!.ipAddress!);
diagnostics['dnsResolution'] = {
'success': true,
'addresses': addresses.map((addr) => addr.address).toList(),
};
} catch (e) {
diagnostics['dnsResolution'] = {
'success': false,
'error': e.toString(),
};
}
// Test de connectivité
final connectionResult = await testConnection(maxRetries: 1);
diagnostics['connectionTest'] = {
'success': connectionResult.isSuccessful,
'message': connectionResult.message,
'responseTime': '${connectionResult.responseTime.inMilliseconds}ms',
'status': connectionResult.status.toString(),
};
// Test de ping réseau (optionnel)
diagnostics['networkReachability'] = await _testNetworkReachability();
}
return diagnostics;
}
/// Test de accessibilité réseau
Future<Map<String, dynamic>> _testNetworkReachability() async {
try {
final result = await Process.run('ping', [
'-c', '1', // Une seule tentative
'-W', '3000', // Timeout 3 secondes
_settings!.ipAddress!,
]);
return {
'success': result.exitCode == 0,
'output': result.stdout,
'error': result.stderr,
};
} catch (e) {
return {
'success': false,
'error': 'Ping non disponible: $e',
};
}
}
/// Met à jour la configuration manuellement
bool updateConfiguration(String ipAddress, int port, {String? name, String? type}) {
if (!_isValidIpAddress(ipAddress)) {
if (kDebugMode) {
print("❌ Adresse IP invalide: $ipAddress");
}
return false;
}
if (port <= 0 || port > 65535) {
if (kDebugMode) {
print("❌ Port invalide: $port");
}
return false;
}
_settings = PrinterSettings(
ipAddress: ipAddress,
port: port,
name: name,
type: type,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
_connectionStatus = PrinterConnectionStatus.disconnected;
if (kDebugMode) {
print("🔧 Configuration mise à jour: $ipAddress:$port");
if (name != null) print("📋 Nom: $name");
if (type != null) print("🏷️ Type: $type");
}
return true;
}
/// Recharge la configuration depuis l'API
Future<bool> reloadConfiguration() async {
if (kDebugMode) {
print("🔄 Rechargement de la configuration depuis l'API...");
}
return await initialize();
}
/// Réinitialise la configuration
void resetConfiguration() {
_settings = null;
_connectionStatus = PrinterConnectionStatus.notConfigured;
if (kDebugMode) {
print("🔄 Configuration de l'imprimante réinitialisée");
}
}
/// Méthode pour normaliser les caractères accentués
String safePadRight(String text, int width) {
// Remplace temporairement les accents par des lettres simples
final normalized = text
.replaceAll(RegExp(r'[éèêë]'), 'e')
.replaceAll(RegExp(r'[àâä]'), 'a')
.replaceAll(RegExp(r'[ôö]'), 'o')
.replaceAll(RegExp(r'[ûü]'), 'u')
.replaceAll(RegExp(r'[ç]'), 'c')
.replaceAll(RegExp(r'[ÉÈÊË]'), 'E')
.replaceAll(RegExp(r'[ÀÂÄ]'), 'A')
.replaceAll(RegExp(r'[ÔÖ]'), 'O')
.replaceAll(RegExp(r'[ÛÜ]'), 'U')
.replaceAll(RegExp(r'[Ç]'), 'C');
await Printing.layoutPdf( return normalized.padRight(width);
onLayout: (PdfPageFormat format) async => pdf.save(), }
);
// Méthodes d'impression optimisées
Future<bool> printOrderESCPOS(Order order) async {
final connectionTest = await testConnection();
if (!connectionTest.isSuccessful) {
if (kDebugMode) {
print("❌ Impossible d'imprimer: ${connectionTest.message}");
}
return false;
}
try {
final socket = await Socket.connect(_settings!.ipAddress!, _settings!.port!);
final List<int> bytes = [];
// Initialisation et configuration pour 80mm
bytes.addAll(ESC_INIT);
bytes.addAll(ESC_PAGE_WIDTH_80MM);
bytes.addAll(ESC_LEFT_MARGIN_0);
bytes.addAll(latin1.encode('${'-' * maxCharsPerLine}\n'));
bytes.addAll(ESC_LEFT_MARGIN_0);
// En-tête centré
bytes.addAll(ESC_ALIGN_CENTER);
bytes.addAll(ESC_BOLD_ON);
bytes.addAll(ESC_LEFT_MARGIN_0);
bytes.addAll(latin1.encode('Commande n° ${order.numeroCommande}\n'));
// Informations commande
bytes.addAll(ESC_LEFT_MARGIN_0);
bytes.addAll(ESC_ALIGN_CENTER);
bytes.addAll(ESC_NORMAL_SIZE);
bytes.addAll(latin1.encode('Table: ${order.tablename}\n'));
bytes.addAll(ESC_BOLD_OFF);
bytes.addAll(latin1.encode('Date: ${_formatTime(order.dateCommande)}\n'));
bytes.addAll(latin1.encode('${'-' * maxCharsPerLine}\n'));
// En-tête du tableau
bytes.addAll(ESC_ALIGN_CENTER);
bytes.addAll(ESC_BOLD_ON);
bytes.addAll(ESC_LEFT_MARGIN_0);
String header = 'Designation'.padRight(maxCharsPerLine - rightWord.length - padding) +
' ' * padding + rightWord + '\n';
bytes.addAll(latin1.encode(header));
bytes.addAll(ESC_BOLD_OFF);
bytes.addAll(latin1.encode('${'-' * maxCharsPerLine}\n'));
// Articles
bytes.addAll(ESC_ALIGN_CENTER);
bytes.addAll(ESC_LEFT_MARGIN_0);
for (var item in order.items) {
String designation = '${item.quantite}x ${item.nom ?? 'Item'}';
int maxDesignationLength = maxCharsPerLine - checkbox.length - padding;
if (designation.length > maxDesignationLength) {
designation = designation.substring(0, maxDesignationLength - 3) + '...';
}
String line = designation.padRight(maxDesignationLength) +
' ' * padding + checkbox + '\n';
bytes.addAll(utf8.encode(line));
}
// Pied de page
bytes.addAll(utf8.encode('${'-' * maxCharsPerLine}\n'));
// Espacement avant coupe
bytes.addAll([0x0A, 0x0A, 0x0A]);
bytes.addAll(ESC_CUT);
socket.add(bytes);
await socket.flush();
await socket.close();
if (kDebugMode) {
print("✅ Impression réussie!");
}
return true;
} catch (e) {
if (kDebugMode) {
print('💥 Erreur impression: $e');
}
return false;
}
}
Future<bool> printOrderESC80MM(Order order) async {
final connectionTest = await testConnection();
if (!connectionTest.isSuccessful) {
if (kDebugMode) {
print("❌ Impossible d'imprimer: ${connectionTest.message}");
}
return false;
}
try {
final socket = await Socket.connect(_settings!.ipAddress!, _settings!.port!);
final List<int> bytes = [];
// Configuration spécifique 80mm
bytes.addAll(ESC_INIT);
bytes.addAll(ESC_PAGE_WIDTH_80MM);
bytes.addAll(ESC_LEFT_MARGIN_0);
// En-tête centré et stylisé
bytes.addAll(ESC_ALIGN_CENTER);
bytes.addAll(ESC_DOUBLE_SIZE);
bytes.addAll(ESC_BOLD_ON);
bytes.addAll(ESC_PAGE_WIDTH_80MM);
bytes.addAll(utf8.encode('COMMANDE\n'));
bytes.addAll(ESC_BOLD_OFF);
bytes.addAll(ESC_NORMAL_SIZE);
bytes.addAll(utf8.encode('${'-' * maxCharsPerLine}\n'));
// Informations commande avec formatage amélioré
bytes.addAll(utf8.encode(_createInfoLine('N° Commande', order.numeroCommande.toString())));
bytes.addAll(utf8.encode(_createInfoLine('Date/Heure', _formatTime(order.dateCommande))));
bytes.addAll(utf8.encode('${'=' * maxCharsPerLine}\n'));
// En-tête du tableau avec colonnes bien définies
bytes.addAll(ESC_BOLD_ON);
bytes.addAll(ESC_PAGE_WIDTH_80MM);
bytes.addAll(utf8.encode(_createTableHeader()));
bytes.addAll(ESC_BOLD_OFF);
bytes.addAll(utf8.encode('${'-' * maxCharsPerLine}\n'));
// Articles avec formatage en colonnes
for (var item in order.items) {
bytes.addAll(utf8.encode(_createItemLine(item.quantite, item.nom ?? 'Item')));
}
// Pied de page
bytes.addAll(utf8.encode('${'=' * maxCharsPerLine}\n'));
bytes.addAll(ESC_ALIGN_CENTER);
bytes.addAll(ESC_PAGE_WIDTH_80MM);
// Coupe avec espacement
bytes.addAll([0x0A, 0x0A]);
bytes.addAll(ESC_CUT);
socket.add(bytes);
await socket.flush();
await socket.close();
return true;
} catch (e) {
if (kDebugMode) {
print('Erreur impression: $e');
}
return false;
}
}
Future<bool> printOrderESCPOSOptimized(Order order) async {
final connectionTest = await testConnection();
if (!connectionTest.isSuccessful) {
if (kDebugMode) {
print("❌ Impossible d'imprimer: ${connectionTest.message}");
}
return false;
}
try {
final socket = await Socket.connect(_settings!.ipAddress!, _settings!.port!);
final List<int> bytes = [];
// Configuration initiale optimisée pour 72mm
bytes.addAll(ESC_INIT);
bytes.addAll(ESC_LEFT_MARGIN_0);
// Définir largeur de page explicite
bytes.addAll([0x1B, 0x51, 180]);
// En-tête
bytes.addAll(ESC_ALIGN_CENTER);
bytes.addAll(ESC_DOUBLE_SIZE);
bytes.addAll(utf8.encode('COMMANDE\n\n'));
// Corps avec police condensée
bytes.addAll(ESC_NORMAL_SIZE);
bytes.addAll(ESC_CONDENSED_ON);
const int compactMaxChars = 32;
bytes.addAll(utf8.encode('Cmd: ${order.numeroCommande}\n'));
bytes.addAll(utf8.encode('Table: ${order.tablename}\n'));
bytes.addAll(utf8.encode('${_formatTime(order.dateCommande)}\n'));
bytes.addAll(utf8.encode('${'=' * compactMaxChars}\n'));
// Articles avec formatage optimisé
for (var item in order.items) {
String qty = '${item.quantite}x';
String name = item.nom ?? 'Item';
int availableSpace = compactMaxChars - qty.length - checkbox.length - 2;
if (name.length > availableSpace) {
name = name.substring(0, availableSpace - 3) + '...';
}
String line = '$qty $name'.padRight(compactMaxChars - checkbox.length) + checkbox + '\n';
bytes.addAll(utf8.encode(line));
}
// Finalisation
bytes.addAll(utf8.encode('${'=' * compactMaxChars}\n'));
bytes.addAll(ESC_CONDENSED_OFF);
bytes.addAll(ESC_ALIGN_CENTER);
bytes.addAll(utf8.encode('\nBon service !\n\n\n'));
bytes.addAll(ESC_CUT);
socket.add(bytes);
await socket.flush();
await socket.close();
return true;
} catch (e) {
if (kDebugMode) {
print('Erreur impression: $e');
}
return false;
}
}
// Fonctions utilitaires pour formatage
String _createInfoLine(String label, String value) {
int labelWidth = 12;
int valueWidth = maxCharsPerLine - labelWidth - 2;
String truncatedLabel = label.length > labelWidth ?
label.substring(0, labelWidth) : label.padRight(labelWidth);
String truncatedValue = value.length > valueWidth ?
value.substring(0, valueWidth - 3) + '...' : value;
return '$truncatedLabel: $truncatedValue\n';
}
String _createTableHeader() {
int qtyWidth = 4;
int checkboxWidth = checkbox.length;
int nameWidth = maxCharsPerLine - qtyWidth - checkboxWidth - 2;
String header = 'Qty'.padRight(qtyWidth) +
'Designation'.padRight(nameWidth) +
checkbox + '\n';
return header;
}
String _createItemLine(int quantity, String itemName) {
int qtyWidth = 4;
int checkboxWidth = checkbox.length;
int nameWidth = maxCharsPerLine - qtyWidth - checkboxWidth - 2;
String qtyStr = quantity.toString().padRight(qtyWidth);
String nameStr = itemName.length > nameWidth ?
itemName.substring(0, nameWidth - 3) + '...' : itemName.padRight(nameWidth);
return qtyStr + nameStr + checkbox + '\n';
}
/// Formatage du temps
String _formatTime(DateTime dateTime) {
return '${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')} ${dateTime.day.toString().padLeft(2, '0')}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.year}';
}
} }
// Fonctions utilitaires pour rétrocompatibilité
Future<bool> printOrderPDF(Order order) async {
final printer = OrderPrinter.instance;
// Initialiser si nécessaire
if (!printer.isConfigured) {
final initialized = await printer.initialize();
if (!initialized) {
if (kDebugMode) {
print('❌ Impossible d\'initialiser l\'imprimante');
}
return false;
}
}
return await printer.printOrderESC80MM(order);
}
Future<void> printOrderWithFeedback(Order order, Function(String, bool) onResult) async {
final printer = OrderPrinter.instance;
try {
// Initialisation si nécessaire
if (!printer.isConfigured) {
onResult('Initialisation de l\'imprimante...', true);
final initialized = await printer.initialize();
if (!initialized) {
onResult('Échec de l\'initialisation', false);
return;
}
}
// Test de connexion
onResult('Test de connexion...', true);
final connectionResult = await printer.testConnection();
if (!connectionResult.isSuccessful) {
onResult(connectionResult.message, false);
return;
}
// Impression
onResult('Impression en cours...', true);
final success = await printer.printOrderESCPOS(order);
if (success) {
onResult('Impression réussie!', true);
} else {
onResult('Échec de l\'impression', false);
}
} catch (e) {
onResult('Erreur: $e', false);
}
}
// Fonction utilitaire pour obtenir des informations sur l'imprimante
Future<Map<String, dynamic>> getPrinterInfo() async {
final printer = OrderPrinter.instance;
if (!printer.isConfigured) {
await printer.initialize();
}
return await printer.runDiagnostics();
}
// Fonction pour tester la connexion avec retour détaillé
Future<ConnectionTestResult> testPrinterConnection() async {
final printer = OrderPrinter.instance;
if (!printer.isConfigured) {
await printer.initialize();
}
return await printer.testConnection();
}

1
lib/services/pdf_service.dart

@ -10,7 +10,6 @@ import 'package:printing/printing.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import '../models/command_detail.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import '../pages/information.dart'; import '../pages/information.dart';

47
lib/services/restaurant_api_service.dart

@ -9,6 +9,7 @@ import 'package:itrimobe/models/command_detail.dart';
import 'package:itrimobe/models/payment_method.dart'; import 'package:itrimobe/models/payment_method.dart';
import 'package:itrimobe/models/tables_order.dart'; import 'package:itrimobe/models/tables_order.dart';
import '../models/printerModel.dart';
import '../pages/information.dart'; import '../pages/information.dart';
class RestaurantApiService { class RestaurantApiService {
@ -59,6 +60,52 @@ static Future<PrintTemplate> getPrintTemplate() async {
throw Exception("Erreur lors de la récupération du template (${response.statusCode})"); throw Exception("Erreur lors de la récupération du template (${response.statusCode})");
} }
} }
static Future<PrinterSettings> getPrinterSettings() async {
final url = Uri.parse('$baseUrl/api/printer/settings');
final response = await http.get(url, headers: _headers);
if (response.statusCode == 200) {
final jsonResponse = json.decode(response.body);
print(jsonResponse);
// print(jsonResponse);
// Adaptez selon la structure de réponse réelle de votre API
if (jsonResponse['data'] is List && (jsonResponse['data'] as List).isNotEmpty) {
return PrinterSettings.fromJson(jsonResponse['data'][0]);
} else if (jsonResponse['data'] is Map) {
return PrinterSettings.fromJson(jsonResponse['data']);
} else {
throw Exception("Aucune configuration d'imprimante trouvée");
}
} else {
throw Exception("Erreur lors de la récupération des paramètres d'imprimante (${response.statusCode})");
}
}
Future<bool> updatePrinterSettings({
required String ipAddress,
required int port,
}) async {
final url = Uri.parse('$baseUrl/api/printer/settings');
final response = await http.put(
url,
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'ip_address': ipAddress,
'port': port,
}),
);
if (response.statusCode == 200) {
// Succès
return true;
} else {
// Erreur
print('Erreur API: ${response.statusCode} - ${response.body}');
return false;
}
}

47
lib/widgets/bottom_navigation.dart

@ -255,7 +255,7 @@ class AppBottomNavigation extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Icon( Icon(
Icons.payment, Icons.history,
color: color:
selectedIndex == 6 selectedIndex == 6
? Colors.white ? Colors.white
@ -299,7 +299,7 @@ class AppBottomNavigation extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Icon( Icon(
Icons.payment, Icons.info,
color: color:
selectedIndex == 7 selectedIndex == 7
? Colors.white ? Colors.white
@ -324,6 +324,49 @@ class AppBottomNavigation extends StatelessWidget {
), ),
), ),
), ),
const SizedBox(width: 20),
GestureDetector(
onTap: () => onItemTapped(8),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color:
selectedIndex == 8
? Colors.green.shade700
: Colors.transparent,
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.settings,
color:
selectedIndex == 8
? Colors.white
: Colors.grey.shade600,
size: 16,
),
const SizedBox(width: 6),
Text(
'Setting',
style: TextStyle(
color:
selectedIndex == 8
? Colors.white
: Colors.grey.shade600,
fontWeight:
selectedIndex == 8
? FontWeight.w500
: FontWeight.normal,
),
),
],
),
),
),
const Spacer(), const Spacer(),

4
linux/flutter/generated_plugin_registrant.cc

@ -6,10 +6,14 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <charset_converter/charset_converter_plugin.h>
#include <printing/printing_plugin.h> #include <printing/printing_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h> #include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) charset_converter_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "CharsetConverterPlugin");
charset_converter_plugin_register_with_registrar(charset_converter_registrar);
g_autoptr(FlPluginRegistrar) printing_registrar = g_autoptr(FlPluginRegistrar) printing_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "PrintingPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "PrintingPlugin");
printing_plugin_register_with_registrar(printing_registrar); printing_plugin_register_with_registrar(printing_registrar);

1
linux/flutter/generated_plugins.cmake

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
charset_converter
printing printing
url_launcher_linux url_launcher_linux
) )

74
pubspec.lock

@ -5,10 +5,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: archive name: archive
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.7" version: "3.6.1"
async: async:
dependency: transitive dependency: transitive
description: description:
@ -49,6 +49,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
charset_converter:
dependency: transitive
description:
name: charset_converter
sha256: a601f27b78ca86c3d88899d53059786d9c3f3c485b64974e9105c06c2569aef5
url: "https://pub.dev"
source: hosted
version: "2.3.0"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@ -81,6 +89,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.6" version: "3.0.6"
csslib:
dependency: transitive
description:
name: csslib
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
cupertino_icons: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@ -89,6 +105,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.8" version: "1.0.8"
esc_pos_printer:
dependency: "direct main"
description:
name: esc_pos_printer
sha256: "312b05f909f3f7dd1e6a3332cf384dcee2c3a635138823654cd9c0133d8b5c45"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
esc_pos_utils:
dependency: "direct main"
description:
name: esc_pos_utils
sha256: "8ec0013d7a7f1e790ced6b09b95ce3bf2c6f9468a3e2bc49ece000761d86c6f8"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -144,6 +176,30 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
gbk_codec:
dependency: transitive
description:
name: gbk_codec
sha256: "3af5311fc9393115e3650ae6023862adf998051a804a08fb804f042724999f61"
url: "https://pub.dev"
source: hosted
version: "0.4.0"
hex:
dependency: transitive
description:
name: hex
sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
html:
dependency: transitive
description:
name: html
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
url: "https://pub.dev"
source: hosted
version: "0.15.6"
http: http:
dependency: "direct main" dependency: "direct main"
description: description:
@ -161,13 +217,13 @@ packages:
source: hosted source: hosted
version: "4.1.2" version: "4.1.2"
image: image:
dependency: transitive dependency: "direct overridden"
description: description:
name: image name: image
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.5.4" version: "4.3.0"
intl: intl:
dependency: "direct main" dependency: "direct main"
description: description:
@ -392,14 +448,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.8" version: "2.1.8"
posix:
dependency: transitive
description:
name: posix
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
url: "https://pub.dev"
source: hosted
version: "6.0.3"
printing: printing:
dependency: "direct main" dependency: "direct main"
description: description:

3
windows/flutter/generated_plugin_registrant.cc

@ -6,12 +6,15 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <charset_converter/charset_converter_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h> #include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <printing/printing_plugin.h> #include <printing/printing_plugin.h>
#include <share_plus/share_plus_windows_plugin_c_api.h> #include <share_plus/share_plus_windows_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
CharsetConverterPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("CharsetConverterPlugin"));
PermissionHandlerWindowsPluginRegisterWithRegistrar( PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
PrintingPluginRegisterWithRegistrar( PrintingPluginRegisterWithRegistrar(

1
windows/flutter/generated_plugins.cmake

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
charset_converter
permission_handler_windows permission_handler_windows
printing printing
share_plus share_plus

Loading…
Cancel
Save