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. 393
      lib/pages/printer_page.dart
  7. 819
      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;
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';
}

6
lib/main.dart

@ -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

@ -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: [
ElevatedButton(
onPressed: () {
printOrderPDF(order);
printOrderWithFeedback(order, (message, isSuccess) {
print('$message - Success: $isSuccess');
});
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder:

4
lib/pages/commandes_screen.dart

@ -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(

393
lib/pages/printer_page.dart

@ -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();
_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,8 +278,23 @@ 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(
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
@ -162,7 +313,7 @@ class _PrinterPageState extends State<PrinterPage> {
const SizedBox(height: 10),
Row(
children: [
if (_isTestingConnection)
if (_isTestingConnection || _isLoadingSettings)
const SizedBox(
width: 20,
height: 20,
@ -184,11 +335,25 @@ class _PrinterPageState extends State<PrinterPage> {
],
),
const SizedBox(height: 10),
ElevatedButton.icon(
onPressed: _isTestingConnection ? null : _testConnection,
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: (_isTestingConnection || _isLoadingSettings) ? null : _testConnection,
icon: const Icon(Icons.refresh),
label: const Text('Tester la connexion'),
),
),
const SizedBox(width: 10),
Expanded(
child: ElevatedButton.icon(
onPressed: _isLoadingSettings ? null : _loadPrinterSettings,
icon: const Icon(Icons.download),
label: const Text('Recharger paramètres'),
),
),
],
),
],
),
),
@ -215,32 +380,119 @@ class _PrinterPageState extends State<PrinterPage> {
borderRadius: BorderRadius.circular(4),
color: Colors.grey[50],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
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: [
const Text(
'REÇU DE VENTE',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
// 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),
),
),
const SizedBox(height: 10),
Align(
alignment: Alignment.centerLeft,
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)}',
),
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 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),
@ -248,14 +500,96 @@ class _PrinterPageState extends State<PrinterPage> {
],
),
),
const SizedBox(height: 10),
const Text('Merci de votre visite !'),
// 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),
@ -263,7 +597,7 @@ class _PrinterPageState extends State<PrinterPage> {
// Bouton d'impression
ElevatedButton.icon(
onPressed:
(_isConnected && !_isPrinting && !_isTestingConnection)
(_isConnected && !_isPrinting && !_isTestingConnection && !_isLoadingSettings)
? _handlePrint
: null,
icon:
@ -288,7 +622,7 @@ class _PrinterPageState extends State<PrinterPage> {
),
),
if (!_isConnected && !_isTestingConnection)
if (!_isConnected && !_isTestingConnection && !_isLoadingSettings)
const Padding(
padding: EdgeInsets.only(top: 10),
child: Text(
@ -300,6 +634,7 @@ class _PrinterPageState extends State<PrinterPage> {
],
),
),
),
);
}
}

819
lib/services/pdf_impression_commande.dart

@ -1,77 +1,764 @@
import 'package:pdf/widgets.dart' as pw;
import 'package:pdf/pdf.dart';
import 'package:printing/printing.dart';
import 'dart:async';
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';
Future<void> printOrderPDF(Order order) async {
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}';
// É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");
}
final pdf = pw.Document();
final pageFormat = PdfPageFormat(
204.1, // 72 mm imprimable
595.0, // 210 mm
marginAll: 0,
if (attempt == maxRetries) {
stopwatch.stop();
_connectionStatus = PrinterConnectionStatus.disconnected;
return ConnectionTestResult(
isSuccessful: false,
message: 'Imprimante inaccessible: ${e.message}',
status: PrinterConnectionStatus.disconnected,
responseTime: stopwatch.elapsed,
);
}
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
],
),
// 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));
}
}
await Printing.layoutPdf(
onLayout: (PdfPageFormat format) async => pdf.save(),
// 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}';
}
}
// 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: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';

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/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;
}
}

47
lib/widgets/bottom_navigation.dart

@ -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
@ -325,6 +325,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(),

4
linux/flutter/generated_plugin_registrant.cc

@ -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);

1
linux/flutter/generated_plugins.cmake

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

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:

3
windows/flutter/generated_plugin_registrant.cc

@ -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(

1
windows/flutter/generated_plugins.cmake

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

Loading…
Cancel
Save