unified_esc_pos_printer 3.0.0 copy "unified_esc_pos_printer: ^3.0.0" to clipboard
unified_esc_pos_printer: ^3.0.0 copied to clipboard

Unified ESC/POS thermal printer package for Flutter. Supports USB, Bluetooth Classic, BLE, and Network connections with a single PrinterManager API

unified_esc_pos_printer #

A unified ESC/POS thermal printer package for Flutter. Supports USB, Bluetooth Classic, BLE, and Network connections through a single PrinterManager API. Includes a full ESC/POS command generator with text formatting, images, barcodes, QR codes, table layouts, cash drawer control, and multilingual text rasterization.

Features #

  • Unified API — Network (TCP/IP), Bluetooth Classic (SPP), BLE, and USB through a single PrinterManager
  • Device discovery — Scan for printers across all connection types simultaneously or filter by type
  • Text formatting — Bold, underline, reverse, alignment, 8 size multipliers, Font A/B
  • Table layouts — Flex-based multi-column rows with per-column styling and text wrapping
  • Image printing — Column format (ESC*) and raster formats (GS v 0 / GS(L) with auto-resizing
  • Barcodes — UPC-A, UPC-E, EAN-13, EAN-8, CODE39, ITF, CODABAR, CODE128
  • QR codes — Native printer QR generation with 8 sizes and 4 error correction levels
  • Text rasterization — Print any script (Chinese, Japanese, Korean, Arabic RTL, etc.) as Flutter-rendered images
  • Cash drawer — Pin 2 and Pin 5 kick commands
  • Beep — Configurable buzzer count and duration
  • Capability profiles — 200+ built-in printer model profiles with code page mappings
  • Connection state tracking — Validated state transitions with broadcast streams
  • Typed exceptions — Granular exception hierarchy for connection, permission, write, and scan errors

Print result

Platform Support #

Connection Android iOS Windows Linux macOS
Network (TCP/IP) Yes Yes Yes Yes Yes
Bluetooth Classic Yes Yes
BLE Yes Yes Yes
USB Yes (OTG) Yes (Print Spooler) Yes (serial) Yes (serial)

Tested Devices #

Device BLE Bluetooth Classic USB
RPP02N Yes Yes Yes

Getting Started #

Installation #

Add to your pubspec.yaml:

dependencies:
  unified_esc_pos_printer: any

Android Setup #

Add the following permissions to android/app/src/main/AndroidManifest.xml:

<!-- Network -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<!-- Bluetooth -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- USB -->
<uses-feature android:name="android.hardware.usb.host" android:required="false" />

iOS Setup #

Add to ios/Runner/Info.plist:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>Required to discover and connect to BLE printers.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Required to discover network printers.</string>

Windows / Linux / macOS #

No additional configuration required for network printers. USB serial ports work out of the box via flutter_libserialport. On Windows, USB printers are accessed through the Print Spooler API.

Quick Start #

import 'package:unified_esc_pos_printer/unified_esc_pos_printer.dart';

final manager = PrinterManager();

// Scan for printers (all connection types, 5-second timeout)
final printers = await manager.scanPrinters(
  timeout: const Duration(seconds: 5),
);

// Connect to the first discovered printer
await manager.connect(printers.first);

// Build a ticket
final ticket = await Ticket.create(PaperSize.mm80);
ticket.text(
  'Hello, Printer!',
  align: PrintAlign.center,
  style: const PrintTextStyle(
    bold: true,
    height: TextSize.size2,
    width: TextSize.size2,
  ),
);
ticket.cut();

// Print and clean up
await manager.printTicket(ticket);
await manager.disconnect();
manager.dispose();

Usage #

Text Formatting #

ticket.text('Normal text');
ticket.text('Bold text', style: const PrintTextStyle(bold: true));
ticket.text('Underline', style: const PrintTextStyle(underline: true));
ticket.text('Reverse', style: const PrintTextStyle(reverse: true));
ticket.text('Bold + Underline', style: const PrintTextStyle(bold: true, underline: true));

// Size multipliers (1x–8x)
ticket.text('Large', style: const PrintTextStyle(height: TextSize.size3, width: TextSize.size3));
ticket.text('Tall only', style: const PrintTextStyle(height: TextSize.size3, width: TextSize.size1));

// Alignment
ticket.text('Left', align: PrintAlign.left);
ticket.text('Center', align: PrintAlign.center);
ticket.text('Right', align: PrintAlign.right);

// Font selection
ticket.text('Font A', style: const PrintTextStyle(fontType: FontType.fontA));
ticket.text('Font B', style: const PrintTextStyle(fontType: FontType.fontB));

// Per-line code table override (must exist in the loaded capability profile)
ticket.text('Café coûté 12,50 €', style: const PrintTextStyle(codeTable: 'CP1252'));

Table Layouts #

Use row() with PrintColumn for multi-column layouts. Columns use flex-based proportional sizing:

// 2-column receipt layout
ticket.row([
  PrintColumn(text: 'Espresso', flex: 2),
  PrintColumn(
    text: '\$3.50',
    flex: 1,
    align: PrintAlign.right,
  ),
]);

// 3-column layout with header
ticket.row([
  PrintColumn(text: 'Item', flex: 5, style: const PrintTextStyle(bold: true)),
  PrintColumn(
    text: 'Qty',
    flex: 3,
    align: PrintAlign.center,
    style: const PrintTextStyle(bold: true),
  ),
  PrintColumn(
    text: 'Total',
    flex: 4,
    align: PrintAlign.right,
    style: const PrintTextStyle(bold: true),
  ),
]);

Separators #

ticket.separator();            // ------------------------------------------------
ticket.separator(char: '=');   // ================================================
ticket.separator(char: '*');   // ************************************************

Images #

import 'package:image/image.dart' as img;

// Load and print an image (raster mode)
final bytes = await rootBundle.load('assets/logo.png');
final image = img.decodeImage(bytes.buffer.asUint8List())!;

ticket.imageRaster(
  image,
  align: PrintAlign.center,
  maxWidth: 400,
  maxHeight: 200,
);

// Column format (ESC*)
ticket.image(image, align: PrintAlign.center);

Barcodes #

Supported types: UPC-A, UPC-E, EAN-13, EAN-8, CODE39, ITF, CODABAR, CODE128.

ticket.barcode(
  '590123412345',
  type: BarcodeType.ean13,
  textPosition: BarcodeTextPosition.below,
);

ticket.barcode(
  '{BABCDEF12345',
  type: BarcodeType.code128,
  textPosition: BarcodeTextPosition.below,
);

QR Codes #

Native printer QR generation with sizes 1–8 and error correction levels L/M/Q/H:

ticket.qrcode('https://example.com', size: QRSize.size5);

ticket.qrcode(
  'https://example.com',
  size: QRSize.size8,
  cor: QRCorrection.H,  // 30% error recovery
);

Text Rasterization #

For scripts not supported by the printer's built-in character tables (CJK, Arabic, Devanagari, Thai, etc.), use textRaster() which renders text using Flutter's text engine and prints it as an image:

await ticket.textRaster('欢迎光临,谢谢惠顾!');       // Chinese
await ticket.textRaster('ようこそ、ありがとう!');     // Japanese
await ticket.textRaster('환영합니다, 감사합니다!');    // Korean
await ticket.textRaster('स्वागत है, धन्यवाद!');          // Hindi

// RTL support
await ticket.textRaster(
  'مرحبا — نص عريض كبير',
  textDirection: TextDirection.rtl,
  align: PrintAlign.right,
);

// Custom styling
await ticket.textRaster(
  'Large Bold Text',
  style: const TextStyle(fontSize: 36, fontWeight: FontWeight.bold),
  align: PrintAlign.center,
);

Cash Drawer & Beep #

// Open cash drawer
ticket.openCashDrawer();                      // Pin 2 (default)
ticket.openCashDrawer(pin: CashDrawer.pin5);  // Pin 5

// Beep
ticket.beep(n: 3, duration: BeepDuration.beep100ms);

Scanning by Connection Type #

// Scan all connection types
final all = await manager.scanPrinters();

// Scan specific types only
final networkOnly = await manager.scanPrinters(
  types: {PrinterConnectionType.network},
);

// Stream-based scanning for progressive results
manager.scanAll(timeout: const Duration(seconds: 5)).listen((devices) {
  print('Found ${devices.length} printers so far');
});

Connecting to Known Devices #

// Network printer by IP
await manager.connect(NetworkPrinterDevice(
  name: 'Kitchen Printer',
  host: '192.168.1.100',
  port: 9100,
));

// BLE printer
await manager.connect(BlePrinterDevice(
  name: 'BLE Printer',
  deviceId: 'AA:BB:CC:DD:EE:FF',
));

// Bluetooth Classic printer
await manager.connect(BluetoothPrinterDevice(
  name: 'BT Printer',
  address: '11:22:33:44:55:66',
));

Connection State Management #

PrinterManager exposes a broadcast stream of connection states:

manager.stateStream.listen((state) {
  switch (state) {
    case PrinterConnectionState.disconnected:
      print('Disconnected');
    case PrinterConnectionState.scanning:
      print('Scanning...');
    case PrinterConnectionState.connecting:
      print('Connecting...');
    case PrinterConnectionState.connected:
      print('Connected');
    case PrinterConnectionState.printing:
      print('Printing...');
    case PrinterConnectionState.disconnecting:
      print('Disconnecting...');
    case PrinterConnectionState.error:
      print('Error');
  }
});

// Quick checks
if (manager.isConnected) { ... }
final device = manager.connectedDevice;

Error Handling #

The package provides a typed exception hierarchy:

try {
  await manager.connect(device);
  await manager.printTicket(ticket);
} on PrinterConnectionException catch (e) {
  print('Connection failed: ${e.message}');
} on PrinterWriteException catch (e) {
  print('Write failed: ${e.message}');
} on PrinterPermissionException catch (e) {
  print('Permission denied: ${e.message}');
} on PrinterNotFoundException catch (e) {
  print('Device not found: ${e.message}');
} on PrinterScanException catch (e) {
  print('Scan failed: ${e.message}');
} on PrinterStateException catch (e) {
  print('Invalid state: ${e.currentState} → ${e.requiredState}');
} on PrinterException catch (e) {
  print('Printer error: ${e.message}');
}

Raw Bytes #

For advanced use cases, send raw ESC/POS command bytes:

// Via Ticket
ticket.rawBytes([0x1B, 0x40]); // ESC @ (initialize)

// Directly to printer
await manager.printBytes([0x1B, 0x40]);

API Reference #

Class Description
PrinterManager Unified facade for scanning, connecting, printing, and cash drawer control
Ticket High-level ticket builder — text, images, barcodes, QR codes, tables
Generator Low-level ESC/POS command generator returning raw byte sequences
PrintTextStyle Immutable text style configuration (bold, underline, size, font)
PrintColumn Column definition for table rows with flex-based sizing
CapabilityProfile Printer capability and code page profile loader
PrinterDevice Abstract base for NetworkPrinterDevice, BlePrinterDevice, BluetoothPrinterDevice, UsbPrinterDevice
PrinterConnectionState Connection state enum with validated transitions
PrinterException Base exception with subclasses for connection, write, permission, scan, state errors
PrinterConnector Abstract connector interface (Network, BLE, Bluetooth, USB implementations)

Paper Sizes #

Size Width (pixels) Font A (chars/line) Font B (chars/line)
58 mm 384 32 42
72 mm 512 42 56
80 mm 576 48 64

Capability Profiles #

The package bundles capability profiles for 200+ thermal printer models, each defining supported ESC/POS code pages. Profiles are loaded automatically when creating a ticket:

// Auto-loads the default bundled profile
final ticket = await Ticket.create(PaperSize.mm80);

// Or load a specific profile
final profile = await CapabilityProfile.load(
  'Generic',
  jsonString: '{"profiles":{"default":{"vendor":"Generic","name":"Generic",'
              '"description":"Generic ESC/POS","codePages":{"0":"CP437"}}}}',
);

final ticket = Ticket(PaperSize.mm80, additionalProfile: profile);

Example App #

A full-featured demo app is included in the example/ directory. It demonstrates:

  • Scanning and filtering by connection type
  • Connecting/disconnecting with state feedback
  • Printing text styles, sizes, alignment, and fonts
  • Multi-column table layouts (2, 3, and 4 columns)
  • Text rasterization for CJK, Arabic, Hindi, Thai, Russian, and European scripts
  • Barcode and QR code generation
  • Image printing (assets and programmatic)
  • Cash drawer and beep commands

Run the example:

cd example
flutter run

License #

BSD 3-Clause License. Copyright (c) 2026, Elriz Technology.

See LICENSE for details.

0
likes
150
points
324
downloads

Publisher

verified publisherelriztechnology.com

Weekly Downloads

Unified ESC/POS thermal printer package for Flutter. Supports USB, Bluetooth Classic, BLE, and Network connections with a single PrinterManager API

Repository (GitHub)
View/report issues

Topics

#esc-pos #esc-pos-printer #thermal-printer #receipt-printer #printer-manager

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

flutter, flutter_libserialport, image, network_info_plus, usb_serial

More

Packages that depend on unified_esc_pos_printer

Packages that implement unified_esc_pos_printer