flutter_xprinter_sdk 1.0.0
flutter_xprinter_sdk: ^1.0.0 copied to clipboard
Flutter plugin for XPrinter thermal receipt and label printers: ESC/POS receipts, TSPL labels, Cyrillic text, image dithering, Bluetooth / USB / TCP.
flutter_xprinter_sdk #
Flutter plugin for XPrinter thermal receipt and label printers — connectivity, ESC/POS receipts, TSPL labels, first-class Cyrillic support, photo dithering, and paper-size-aware receipt layout helpers (58 / 72 / 80 mm).
Bundled native SDK versions #
This release bundles the official XPrinter native SDKs with the plugin, so you do not need to download them separately or run a setup script.
| Platform | Bundled XPrinter SDK |
|---|---|
| iOS | XPrinter PrinterSDK v2.3.0 (libPrinterSDK.a + Headers/) |
| Android | XPrinter printer-lib-3.2.0.aar |
| Windows 10+ | XPrinter ESC/POS Windows SDK v2.0.4 (printer.sdk.dll) |
Tested on XP-58IIT (58 mm BLE) and XP-C260M (80 mm BLE / USB / Ethernet).
Sample output #
Receipt printed by the bundled example/ app (58 mm thermal):
Cyrillic text, dotted dividers, space-aligned price columns, dithered photo, QR code, and barcode — all from a single demo Dart file (~200 lines). See example/lib/main.dart.
Why this plugin #
Most ESC/POS Flutter packages target Latin text on a single paper width. This one solves the harder problems that come up in production receipts:
- Cyrillic that doesn't garble. The Android Charset API silently returns
?for CP866 on many devices; this plugin's pure-DartencodeToCp866works on every device. - Logos that look crisp instead of stippled. Built-in Floyd-Steinberg dithering for photos and hard threshold for logos, with content auto-detection.
- One layout, many paper sizes.
XprinterLayout.configure(paperSizeMm: 80)adapts character count, divider width, and image scaling. Same code drives 58 mm and 80 mm prints. - Chinese-market printers. Sends
FS .to cancel multi-byte mode before applying CP866, so models like the XP-C260M print Cyrillic correctly out of the box.
Installation #
1. Add the dependency #
dependencies:
flutter_xprinter_sdk: ^1.0.0
flutter pub get
2. Install platform dependencies #
The iOS static library and headers, Android AAR, and Windows DLL are bundled with the plugin and linked into the host application automatically.
cd ios && pod install && cd ..
flutter clean && flutter run
That's it.
If you install from pub.dev, there is no separate XPrinter download or vendor setup step.
3. Permissions #
Android. The plugin's AndroidManifest.xml already declares the Bluetooth,
USB, and Internet permissions. However, on Android 12+ (API 31+) you must
request BLUETOOTH_SCAN and BLUETOOTH_CONNECT at runtime before calling any
scan/connect method. On Android 11 and older, Bluetooth discovery results also
require runtime location permission. The example app uses
permission_handler:
import 'package:permission_handler/permission_handler.dart';
await [
Permission.bluetoothScan,
Permission.bluetoothConnect,
].request();
await Permission.locationWhenInUse.request(); // Android <= 11 discovery
iOS. Add to your app's Info.plist:
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Used to connect to thermal receipt printers.</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Used to connect to thermal receipt printers.</string>
Windows. The vendor SDK supports USB, TCP/IP, and serial ports. This plugin currently exposes USB and TCP through the existing connection API. Direct Bluetooth discovery and MAC-address connections are not available on Windows.
Quick start #
import 'dart:typed_data';
import 'package:flutter_xprinter_sdk/flutter_xprinter_sdk.dart';
Future<void> printSimpleReceipt() async {
// 1. List paired Bluetooth devices and pick one. For live advertising
// devices use `XprinterBluetooth.startDiscovery(timeout:)` instead.
final devices = await XprinterBluetooth.getBondedDevices();
final printer = devices.first;
await XprinterConnection.connect(
type: XprinterConnectionType.bluetooth,
address: printer.address,
);
// 2. Initialise.
await PosPrinter.initialize();
await PosPrinter.sendRawCommand(Uint8List.fromList(<int>[0x1C, 0x2E])); // FS . (cancel Chinese mode)
await PosPrinter.selectCodePage(XprinterCodePage.pc866);
XprinterLayout.configure(paperSizeMm: 58);
// 3. Print.
await XprinterLayout.printLine('Спасибо за покупку!', alignment: XprinterAlignment.center);
await XprinterLayout.printSectionDivider();
await XprinterLayout.printValueRow('Bonaqua 0,5 л', '8 000 UZS');
await XprinterLayout.printBoldRow('ИТОГО', '8 000 UZS');
// 4. Cut & disconnect.
await PosPrinter.feedLine(3);
await PosPrinter.cutPaper();
await XprinterConnection.disconnect();
}
See example/ for a full sample app.
Label printing (XP-245B and other TSPL models) #
The XP-245B has a 48 mm print head at 203 dpi (about 384 dots). The commands follow the TSPL/TSPL2 programming manual. Connect in label mode, then create and print a label:
await XprinterConnection.connect(
type: XprinterConnectionType.bluetooth,
address: printer.address,
labelMode: true,
);
final label = LabelPrinter(widthMm: 48, heightMm: 30, gapMm: 2)
..addText('Milk 1L', x: 16, y: 16, font: 3)
..addBarcode('123456789012', x: 16, y: 60, height: 80)
..addQrCode('SKU-123', x: 280, y: 16);
await label.print(copies: 2);
Coordinates and image dimensions are in dots. addImage accepts PNG, JPEG,
BMP, or GIF bytes and converts them to the TSPL one-bit BITMAP format. Use it
for logos or rendered Unicode text. The built-in printer font used by
addText accepts ASCII only; render Cyrillic to an image before adding it to
the label. build() returns the complete command bytes if you need to inspect
or send them through another transport. Label jobs are sent in one native call,
which also flushes the iOS BLE buffer. Use the printer's label mode and paper
gap setting; receipt helpers and PosPrinter.initialize() are for ESC/POS jobs.
The XP-245B can also operate in receipt mode, so make sure the installed media and printer mode match the TSPL label job.
To test with the bundled app, connect a physical Android or iOS device, run
flutter devices, then cd example && flutter run -d <device-id>. Scan for the
printer, select it, enter your label width,
height, and gap (defaults: 48 × 30 mm, 2 mm), then tap Print demo label
(TSPL). The iOS Simulator cannot send print commands because the bundled
vendor SDK has no arm64 simulator slice. The plugin has no macOS or web
implementation.
API reference #
Connection — XprinterConnection / XprinterBluetooth #
| Call | Purpose |
|---|---|
XprinterBluetooth.getBondedDevices() |
Returns already-paired BT devices (Future<List<…>>). |
XprinterBluetooth.startDiscovery(timeout:) |
Streams advertising BT devices live (Stream<…>). |
XprinterConnection.connect(type:, address:) |
Connects via BT / USB / TCP. |
XprinterConnection.disconnect() |
Closes the active connection. |
XprinterConnection.isConnected() |
Polls connection state. |
Low-level commands — PosPrinter #
| Call | Purpose |
|---|---|
PosPrinter.initialize() |
ESC @ reset. |
PosPrinter.selectCodePage(page) |
Sets the active code page (CP437, CP866, etc.). |
PosPrinter.printText(...) / printColumnsText(...) |
Plain text / multi-column text. |
PosPrinter.printBitmap(bytes, alignment, widthDots) |
Raster image. |
PosPrinter.printQRCode(content, moduleSize, level, alignment) |
QR code. |
PosPrinter.printBarCode(...) |
Code128 / EAN13 / UPC-A barcodes. |
PosPrinter.printHorizontalLine(widthDots, heightRows, alignment) |
Solid black bar. |
PosPrinter.feedLine(n) / cutPaper(half:) |
Paper feed and cut. |
PosPrinter.sendRawCommand(bytes) |
Escape hatch for arbitrary ESC/POS bytes. |
Labels — LabelPrinter #
| Call | Purpose |
|---|---|
LabelPrinter(widthMm:, heightMm:, gapMm:) |
Set label stock size and gap. |
addText(...) / addBarcode(...) / addQrCode(...) |
Place TSPL text, Code 128, and QR code. |
addImage(bytes, x:, y:) |
Place a monochrome raster image. |
print(copies:) |
Print one format with the requested number of copies. |
build(copies:) |
Return the TSPL byte stream without sending it. |
Layout helpers — XprinterLayout #
Paper-size-aware text composition. Call XprinterLayout.configure(paperSizeMm:) once per print job before any other helper.
| Helper | Layout |
|---|---|
printLine(text, alignment:, bold:) |
One line of text, CP866-encoded. |
printInfoRow(label, value) |
Label: value with bold label, regular value. |
printValueRow(label, value) |
label .... value with regular label and bold value. |
printPlainRow(label, value) |
label .... value, both regular weight. |
printBoldRow(label, value) |
Fully bold (ИТОГО / section headers). |
printDiscountRow(label, value) |
Alias for printValueRow. |
printSectionDivider() |
Solid horizontal line, paper-width. |
printAssetIcon(assetPath, heightDots:, alignment:) |
Cached PNG asset, sized to dots. |
printIconTextRow(entries) |
(icon + text) pairs side-by-side, wrap to fit. |
Cyrillic — encodeToCp866 #
final bytes = encodeToCp866('Спасибо'); // → CP866 bytes
Pure-Dart lookup, no platform dependency. Use it directly when you need to bypass XprinterLayout for custom rows.
Image preparation — XprinterImageLoader #
// From a remote URL (e.g. merchant logo on a CDN).
final bytes = await XprinterImageLoader.fromUrl(
url: 'https://example.com/logo.png',
targetWidthDots: 384,
mode: XprinterImageMode.auto, // or .threshold for logos, .dither for photos
);
if (bytes != null) {
await PosPrinter.printBitmap(bytes, widthDots: 384);
}
// From a Flutter asset.
final iconBytes = await XprinterImageLoader.fromAsset(
assetPath: 'assets/printer/store_logo.png',
targetWidthDots: 384,
);
// From already-loaded bytes.
final binarised = XprinterImageLoader.fromBytes(
bytes: rawPngBytes,
targetWidthDots: 384,
);
All three return PNG bytes ready for PosPrinter.printBitmap. The image is downloaded / decoded → resized to targetWidthDots preserving aspect → flattened over white → binarised.
Dithering — XprinterImageDither #
Lower-level access to the binarisation pipeline if you want to compose images yourself before binarising:
final composed = img.Image(width: 384, height: 200);
// ... draw into [composed] ...
final pngBytes = XprinterImageDither.binarise(composed, mode: XprinterImageMode.threshold);
XprinterImageMode:
.auto— counts mid-tone pixels, picks threshold for logos / dither for photos..threshold— hard cutoff, sharp edges. Best for logos..dither— Floyd-Steinberg, smooth tone reproduction. Best for photos.
Paper size #
paperSizeMm |
Chars/line (Font A) | Print head dots |
|---|---|---|
| 58 | 32 | 384 |
| 72 | 42 | 512 |
| 80 | 48 | 576 |
Troubleshooting #
The receipt prints Chinese characters where Cyrillic should be.
Some 80 mm models (XP-C260M, etc.) start in multi-byte Chinese mode. Send FS . (0x1C 0x2E) before selectCodePage:
await PosPrinter.initialize();
await PosPrinter.sendRawCommand(Uint8List.fromList(<int>[0x1C, 0x2E]));
await PosPrinter.selectCodePage(XprinterCodePage.pc866);
Image prints as a solid black square.
The image had transparent pixels and the SDK rendered them as black. XprinterImageLoader handles this for you (flattens over white). If you're calling PosPrinter.printBitmap directly with a PNG that has alpha, flatten it first.
Logo prints stippled instead of solid black.
Pass mode: XprinterImageMode.threshold to force the hard-threshold path. Auto-detection can misclassify small upscaled icons as photo-like.
net.posprinter.* not found at link time on Android.
Make sure the plugin package contains
android/libs/printer-lib-3.2.0.aar, then run flutter clean and rebuild.
Windows build says printer.sdk.dll was not found.
Make sure the plugin package contains
windows/xprinter_sdk/x64/printer.sdk.dll, then run flutter clean and
rebuild the application.
Licence #
This wrapper is MIT-licensed (see LICENSE).
The iOS, Android, and Windows SDK binaries are included in this distribution. Review XPrinter Co., Ltd.'s licence terms before shipping the vendor binaries in your app.
Contributing #
Issues and pull requests welcome at https://github.com/Lazizbek97/flutter_xprinter_sdk.