A comprehensive Flutter plugin for POS (Point of Sale) and label printers, supporting ESC/POS receipt printers, ZPL label printers, and TSPL label printers with USB and network connectivity.
- Multiple Printer Types: Support for ESC/POS receipt printers, ZPL label printers, and TSPL label printers
- Multiple Connection Types: USB, network (TCP), and SDK-based discovery
- HTML Printing: Convert HTML to printable bitmaps for receipt and label printers
- Raw Data Printing: Send raw ESC/POS, ZPL, CPCL, and TSPL commands
- Printer Discovery: Automatic discovery of USB and network printers
- Connection Management: Robust connection handling with retry logic and proper cleanup
- Paper Sizes: Support for 58mm (416 dots) and 80mm (576 dots) paper widths
- Network Configuration: Configure printer network settings via USB or UDP
- Real-time Events: Monitor printer attach/detach events and discovery status
- Stress Testing: Built-in support for concurrent printing operations
Add this to your package's pubspec.yaml file:
dependencies:
pos_printers:
git:
url: https://github.com/your-repo/pos_printers.gitimport 'package:pos_printers/pos_printers.dart';
// Create manager instance
final manager = PosPrintersManager();
// Discover printers
final printerStream = manager.findPrinters(filter: null);
printerStream.listen((printer) {
print('Found printer: ${printer.id}');
});
// Print HTML receipt on 80mm paper
await manager.printEscHTML(
printer,
'<html><body><h1>Receipt</h1><p>Total: $10.50</p></body></html>',
PaperSize.mm80.value
);Main class for managing printer operations.
final manager = PosPrintersManager();Methods:
Stream<PrinterConnectionParamsDTO> findPrinters({PrinterDiscoveryFilter? filter})Future<void> awaitDiscoveryComplete()Future<StatusResult> getPrinterStatus(PrinterConnectionParamsDTO printer)Future<StringResult> getPrinterSN(PrinterConnectionParamsDTO printer)Future<void> openCashBox(PrinterConnectionParamsDTO printer)Future<void> printEscHTML(PrinterConnectionParamsDTO printer, String html, int width)Future<void> printEscRawData(PrinterConnectionParamsDTO printer, Uint8List data, int width)Future<void> printZplHtml(PrinterConnectionParamsDTO printer, String html, int width)Future<void> printZplRawData(PrinterConnectionParamsDTO printer, Uint8List data, int width)Future<ZPLStatusResult> getZPLPrinterStatus(PrinterConnectionParamsDTO printer)Future<void> printTsplHtml(PrinterConnectionParamsDTO printer, String html, int width)Future<void> printTsplRawData(PrinterConnectionParamsDTO printer, Uint8List data, int width)Future<TSPLStatusResult> getTSPLPrinterStatus(PrinterConnectionParamsDTO printer)Future<void> setNetSettings(PrinterConnectionParamsDTO printer, NetworkParams netSettings)Future<void> configureNetViaUDP(String macAddress, NetworkParams netSettings)void dispose()
Streams:
Stream<PrinterConnectionParamsDTO> discoveryStream- Discovered printers during scanningStream<PrinterConnectionEvent> connectionEvents- Printer attach/detach events
Represents printer connection parameters.
final printer = PrinterConnectionParamsDTO(
id: 'printer_001',
connectionType: PosPrinterConnectionType.network,
usbParams: null,
networkParams: NetworkParams(
ipAddress: '192.168.1.100',
mask: '255.255.255.0',
gateway: '192.168.1.1',
macAddress: 'AA:BB:CC:DD:EE:FF',
dhcp: false,
),
);Properties:
String id- Unique printer identifierPosPrinterConnectionType connectionType- Connection type (usb, network)UsbParams? usbParams- USB connection parameters (if USB)NetworkParams? networkParams- Network connection parameters (if network)
Network connection parameters.
final networkParams = NetworkParams(
ipAddress: '192.168.1.100',
mask: '255.255.255.0',
gateway: '192.168.1.1',
macAddress: 'AA:BB:CC:DD:EE:FF',
dhcp: false,
);USB connection parameters.
final usbParams = UsbParams(
vendorId: 0x0416,
productId: 0x5011,
serialNumber: 'ABC123',
manufacturer: 'Xprinter',
productName: 'XP-80C',
);// Discover all available printers
final printerStream = manager.findPrinters(filter: null);
printerStream.listen(
(printer) => print('Found: ${printer.id}'),
onDone: () => print('Discovery complete'),
onError: (error) => print('Discovery error: $error'),
);
// Wait for discovery to complete
await manager.awaitDiscoveryComplete();// Discover only USB printers
final filter = PrinterDiscoveryFilter(
connectionTypes: [DiscoveryConnectionType.usb],
);
final printerStream = manager.findPrinters(filter: filter);DiscoveryConnectionType.usb- USB connected printersDiscoveryConnectionType.sdk- Network printers via Xprinter SDKDiscoveryConnectionType.tcp- Network printers via TCP (port 9100)
// Monitor printer connections
manager.connectionEvents.listen((event) {
switch (event.type) {
case PrinterConnectionEventType.attached:
print('Printer attached: ${event.printer?.id}');
break;
case PrinterConnectionEventType.detached:
print('Printer detached: ${event.printer?.id}');
break;
}
});Print HTML content converted to bitmap.
ESC/POS Receipt Printers:
final html = '''
<html>
<body style="font-family: monospace; text-align: center;">
<h2>STORE RECEIPT</h2>
<hr>
<table width="100%">
<tr><td>Coffee</td><td align="right">$3.50</td></tr>
<tr><td>Sandwich</td><td align="right">$7.00</td></tr>
</table>
<hr>
<p><b>Total: $10.50</b></p>
<p>Thank you!</p>
</body>
</html>
''';
await manager.printEscHTML(printer, html, PaperSize.mm80.value);ZPL Label Printers:
final html = '''
<html>
<body style="font-family: Arial; padding: 10px;">
<div style="border: 2px solid black; padding: 20px;">
<h1>SHIPPING LABEL</h1>
<p>To: John Doe</p>
<p>123 Main Street</p>
<p>City, State 12345</p>
<div style="margin-top: 20px; font-size: 24px;">
<b>Tracking: ABC123456789</b>
</div>
</div>
</body>
</html>
''';
await manager.printZplHtml(printer, html, PaperSize.mm80.value);Send raw printer commands directly.
ESC/POS Commands:
final escCommands = [
0x1B, 0x40, // Initialize
0x1B, 0x61, 0x01, // Center align
...utf8.encode('Hello World'),
0x0A, // Line feed
0x1D, 0x56, 0x42, 0x00, // Cut paper
];
await manager.printEscRawData(
printer,
Uint8List.fromList(escCommands),
PaperSize.mm80.value
);ZPL Commands:
final zplCommands = '''
^XA
^CFD,30
^FO50,50^FDHello World^FS
^XZ
''';
await manager.printZplRawData(
printer,
Uint8List.fromList(utf8.encode(zplCommands)),
PaperSize.mm80.value
);TSPL Commands:
final tsplCommands = '''
SIZE 60 mm, 40 mm
GAP 2 mm, 0 mm
DIRECTION 0
CLS
TEXT 50,50,"3",0,1,1,"Hello World"
PRINT 1
''';
await manager.printTsplRawData(
printer,
Uint8List.fromList(utf8.encode(tsplCommands)),
PaperSize.mm58.value
);TSPL HTML Printing:
final html = '''
<html>
<head>
<style>
body { font-family: Arial; padding: 10px; }
h1 { font-size: 24px; text-align: center; }
</style>
</head>
<body>
<h1>Product Label</h1>
<p>SKU: 12345</p>
<p>Price: $19.99</p>
</body>
</html>
''';
await manager.printTsplHtml(printer, html, PaperSize.mm58.value);Predefined paper sizes with dot widths:
enum PaperSize {
mm58(416), // 58mm paper = 416 dots
mm80(576); // 80mm paper = 576 dots
}
// Usage
await manager.printEscHTML(printer, html, PaperSize.mm58.value);
await manager.printEscHTML(printer, html, PaperSize.mm80.value);final status = await manager.getPrinterStatus(printer);
if (status.success) {
print('Printer status: ${status.status}');
} else {
print('Error: ${status.errorMessage}');
}final snResult = await manager.getPrinterSN(printer);
if (snResult.success) {
print('Serial Number: ${snResult.value}');
} else {
print('Error: ${snResult.errorMessage}');
}final zplStatus = await manager.getZPLPrinterStatus(printer);
if (zplStatus.success) {
print('ZPL Status Code: ${zplStatus.code}');
// Status codes: 00-80 (see ZPL documentation)
} else {
print('Error: ${zplStatus.errorMessage}');
}final tsplStatus = await manager.getTSPLPrinterStatus(printer);
if (tsplStatus.success) {
print('TSPL Status Code: ${tsplStatus.code}');
// Status codes:
// 0x00 - Normal
// 0x01 - Head opened
// 0x04 - Out of paper
// 0x08 - Out of ribbon
// See TSPL documentation for complete list
} else {
print('Error: ${tsplStatus.errorMessage}');
}final newSettings = NetworkParams(
ipAddress: '192.168.1.200',
mask: '255.255.255.0',
gateway: '192.168.1.1',
macAddress: null, // Will be detected
dhcp: false,
);
await manager.setNetSettings(usbPrinter, newSettings);final settings = NetworkParams(
ipAddress: '192.168.1.201',
mask: '255.255.255.0',
gateway: '192.168.1.1',
macAddress: 'AA:BB:CC:DD:EE:FF',
dhcp: false,
);
await manager.configureNetViaUDP('AA:BB:CC:DD:EE:FF', settings);// Open cash drawer connected to printer
await manager.openCashBox(printer);All async methods can throw exceptions. Wrap in try-catch blocks:
try {
await manager.printEscHTML(printer, html, PaperSize.mm80.value);
print('Print successful');
} catch (e) {
print('Print failed: $e');
}Future<void> stressTest() async {
final printers = <PrinterConnectionParamsDTO>[printer1, printer2];
final futures = <Future<void>>[];
// Send 10 print jobs simultaneously
for (int i = 0; i < 10; i++) {
final html = '<html><body><h1>Receipt #$i</h1></body></html>';
futures.add(manager.printEscHTML(
printers[i % printers.length],
html,
PaperSize.mm80.value,
));
}
// Wait for all to complete
await Future.wait(futures);
print('All prints completed');
}class PrinterService {
final PosPrintersManager _manager;
final Map<String, PrinterConnectionParamsDTO> _connectedPrinters = {};
PrinterService() : _manager = PosPrintersManager() {
_setupEventListeners();
}
void _setupEventListeners() {
_manager.connectionEvents.listen((event) {
switch (event.type) {
case PrinterConnectionEventType.attached:
_connectedPrinters[event.printer!.id] = event.printer!;
break;
case PrinterConnectionEventType.detached:
_connectedPrinters.remove(event.printer!.id);
break;
}
});
}
Future<void> printToAll(String html) async {
final futures = _connectedPrinters.values.map((printer) =>
_manager.printEscHTML(printer, html, PaperSize.mm80.value));
await Future.wait(futures);
}
}See the example/ directory for a full working application that demonstrates:
- Printer discovery with real-time updates
- HTML and raw data printing
- Stress testing with concurrent operations
- Error handling and retry logic
- Network printer configuration
- Multi-language support (ESC/POS and ZPL)
usb- USB connectionnetwork- Network TCP/IP connection
esc- ESC/POS commands (receipt printers)zpl- ZPL commands (label printers)
usb- Discover USB printerssdk- Discover via Xprinter SDKtcp- Discover via TCP scan (port 9100)
attached- Printer was connecteddetached- Printer was disconnected
The plugin uses a modular architecture with:
- Pigeon-generated bindings for type-safe Flutter-Android communication
- Connection pooling with automatic retry and cleanup
- Thread-safe operations using Kotlin coroutines
- Event-driven discovery with real-time printer detection
- Robust error handling with detailed error messages
- Android: Full support for USB and network printers
- iOS: Not supported (Android-only plugin)
To regenerate the Pigeon bindings after modifying pigeons/pos_printers.dart:
dart run pigeon --input pigeons/pos_printers.dartThis project is licensed under the MIT License - see the LICENSE file for details.