commit 09082025
This commit is contained in:
parent
2ab6254576
commit
3c8dfbcac7
@ -40,6 +40,8 @@ class _MainLayoutState extends State<MainLayout> {
|
||||
return 6;
|
||||
case '/information':
|
||||
return 7;
|
||||
case '/Setting':
|
||||
return 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -76,6 +78,9 @@ class _MainLayoutState extends State<MainLayout> {
|
||||
case 7:
|
||||
route = '/information';
|
||||
break;
|
||||
case 8:
|
||||
route = '/Setting';
|
||||
break;
|
||||
default:
|
||||
route = '/tables';
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import 'pages/login_screen.dart';
|
||||
import 'pages/menus_screen.dart';
|
||||
import 'pages/historique_commande.dart';
|
||||
import 'pages/information.dart';
|
||||
import 'pages/printer_page.dart';
|
||||
import 'pages/encaissement_screen.dart'; // NOUVEAU
|
||||
|
||||
void main() {
|
||||
@ -64,6 +65,11 @@ class MyApp extends StatelessWidget {
|
||||
currentRoute: '/information',
|
||||
child: PrintTemplateManagementScreen(),
|
||||
),
|
||||
'/Setting':
|
||||
(context) => MainLayout(
|
||||
currentRoute: '/Setting',
|
||||
child: PrinterPage(),
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
52
lib/models/printerModel.dart
Normal file
52
lib/models/printerModel.dart
Normal file
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -255,7 +255,9 @@ class _CartPageState extends State<CartPage> {
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
printOrderPDF(order);
|
||||
printOrderWithFeedback(order, (message, isSuccess) {
|
||||
print('$message - Success: $isSuccess');
|
||||
});
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
MaterialPageRoute(
|
||||
builder:
|
||||
|
||||
@ -615,7 +615,9 @@ class OrderCard extends StatelessWidget {
|
||||
// ← Bouton imprimer
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
printOrderPDF(order);
|
||||
printOrderWithFeedback(order, (message, isSuccess) {
|
||||
print('$message - Success: $isSuccess');
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.print, color: Colors.white, size: 16),
|
||||
label: const Text(
|
||||
|
||||
@ -2,6 +2,8 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
// Ajoutez l'import de votre service API et du modèle
|
||||
import '../services/restaurant_api_service.dart';
|
||||
|
||||
class PrinterPage extends StatefulWidget {
|
||||
const PrinterPage({super.key});
|
||||
@ -11,18 +13,55 @@ class PrinterPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _PrinterPageState extends State<PrinterPage> {
|
||||
static const String printerIP = '192.168.123.251';
|
||||
static const int printerPort = 9100;
|
||||
// Remplacer les constantes par des variables
|
||||
String printerIP = '192.168.123.251'; // Valeur par défaut
|
||||
int printerPort = 9100; // Valeur par défaut
|
||||
|
||||
bool _isPrinting = false;
|
||||
bool _isConnected = false;
|
||||
bool _isTestingConnection = false;
|
||||
bool _isLoadingSettings = false;
|
||||
String _statusMessage = 'Non testé';
|
||||
|
||||
@override
|
||||
void 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 {
|
||||
@ -135,6 +174,103 @@ class _PrinterPageState extends State<PrinterPage> {
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@ -142,164 +278,363 @@ class _PrinterPageState extends State<PrinterPage> {
|
||||
title: const Text('Imprimante POS'),
|
||||
backgroundColor: Colors.blue,
|
||||
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(
|
||||
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],
|
||||
body: SingleChildScrollView(
|
||||
child: 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,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
children: [
|
||||
const Text(
|
||||
'REÇU DE VENTE',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
if (_isTestingConnection || _isLoadingSettings)
|
||||
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),
|
||||
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<Color>(
|
||||
Colors.white,
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: (_isTestingConnection || _isLoadingSettings) ? null : _testConnection,
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: const Text('Tester la connexion'),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: 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,
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: _isLoadingSettings ? null : _loadPrinterSettings,
|
||||
icon: const Icon(Icons.download),
|
||||
label: const Text('Recharger paramètres'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
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: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Table(
|
||||
columnWidths: const {
|
||||
0: FlexColumnWidth(3), // Colonne gauche large
|
||||
1: FlexColumnWidth(2), // Colonne milieu
|
||||
2: FlexColumnWidth(1), // Colonne droite pour le bouton
|
||||
},
|
||||
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(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Bouton d'impression
|
||||
ElevatedButton.icon(
|
||||
onPressed:
|
||||
(_isConnected && !_isPrinting && !_isTestingConnection && !_isLoadingSettings)
|
||||
? _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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,77 +1,764 @@
|
||||
import 'package:pdf/widgets.dart' as pw;
|
||||
import 'package:pdf/pdf.dart';
|
||||
import 'package:printing/printing.dart';
|
||||
import '../pages/commandes_screen.dart';
|
||||
Future<void> printOrderPDF(Order order) async {
|
||||
import 'dart:async';
|
||||
|
||||
String _formatTime(DateTime dateTime) {
|
||||
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';
|
||||
|
||||
// Énumération pour les différents états de connexion
|
||||
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);
|
||||
}
|
||||
|
||||
/// Test de connexion avancé avec métriques
|
||||
Future<ConnectionTestResult> testConnection({
|
||||
Duration timeout = const Duration(seconds: 5),
|
||||
int maxRetries = 3,
|
||||
}) async {
|
||||
if (!isConfigured) {
|
||||
return const ConnectionTestResult(
|
||||
isSuccessful: false,
|
||||
message: 'Imprimante non configurée',
|
||||
status: PrinterConnectionStatus.notConfigured,
|
||||
responseTime: Duration.zero,
|
||||
);
|
||||
}
|
||||
|
||||
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');
|
||||
|
||||
return normalized.padRight(width);
|
||||
}
|
||||
|
||||
// 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}';
|
||||
}
|
||||
final pdf = pw.Document();
|
||||
|
||||
final pageFormat = PdfPageFormat(
|
||||
204.1, // 72 mm imprimable
|
||||
595.0, // 210 mm
|
||||
marginAll: 0,
|
||||
);
|
||||
|
||||
pdf.addPage(
|
||||
pw.Page(
|
||||
pageFormat: pageFormat,
|
||||
build: (pw.Context context) {
|
||||
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
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await Printing.layoutPdf(
|
||||
onLayout: (PdfPageFormat format) async => pdf.save(),
|
||||
);
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
@ -10,7 +10,6 @@ import 'package:printing/printing.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import '../models/command_detail.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../pages/information.dart';
|
||||
|
||||
@ -9,6 +9,7 @@ import 'package:itrimobe/models/command_detail.dart';
|
||||
import 'package:itrimobe/models/payment_method.dart';
|
||||
import 'package:itrimobe/models/tables_order.dart';
|
||||
|
||||
import '../models/printerModel.dart';
|
||||
import '../pages/information.dart';
|
||||
|
||||
class RestaurantApiService {
|
||||
@ -59,6 +60,52 @@ static Future<PrintTemplate> getPrintTemplate() async {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -255,7 +255,7 @@ class AppBottomNavigation extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.payment,
|
||||
Icons.history,
|
||||
color:
|
||||
selectedIndex == 6
|
||||
? Colors.white
|
||||
@ -299,7 +299,7 @@ class AppBottomNavigation extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.payment,
|
||||
Icons.info,
|
||||
color:
|
||||
selectedIndex == 7
|
||||
? 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(),
|
||||
|
||||
@ -6,10 +6,14 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <charset_converter/charset_converter_plugin.h>
|
||||
#include <printing/printing_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
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 =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "PrintingPlugin");
|
||||
printing_plugin_register_with_registrar(printing_registrar);
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
charset_converter
|
||||
printing
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
74
pubspec.lock
74
pubspec.lock
@ -5,10 +5,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
|
||||
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.7"
|
||||
version: "3.6.1"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -49,6 +49,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -81,6 +89,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csslib
|
||||
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -89,6 +105,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -144,6 +176,30 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -161,13 +217,13 @@ packages:
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
image:
|
||||
dependency: transitive
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: image
|
||||
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
|
||||
sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.4"
|
||||
version: "4.3.0"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -392,14 +448,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
posix:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: posix
|
||||
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.3"
|
||||
printing:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@ -6,12 +6,15 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <charset_converter/charset_converter_plugin.h>
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
#include <printing/printing_plugin.h>
|
||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
CharsetConverterPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("CharsetConverterPlugin"));
|
||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||
PrintingPluginRegisterWithRegistrar(
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
charset_converter
|
||||
permission_handler_windows
|
||||
printing
|
||||
share_plus
|
||||
|
||||
Loading…
Reference in New Issue
Block a user