thermal_printer_billbay
A comprehensive Flutter plugin for ESC/POS thermal printers via Bluetooth Classic (SPP).
Supports Android (Bluetooth Classic) and iOS (MFi via ExternalAccessory).
Features
- Bluetooth Classic (SPP) connection — scan, pair, connect, send
- Full ESC/POS command set — text, bold, underline, reverse, font sizes, alignment
- Arabic text shaping — automatic joining forms, Lam-Alef ligatures, bidi reversal
- Multiple code pages — Win-1256, CP864, CP720, UTF-8, or any custom number
- Paper sizes — 58mm, 72mm, 80mm, 110mm, and custom
- Multi-column rows — grid-based column layout for receipts
- Receipt templates — ready-made invoice builder with items, tax, QR, footer
- Images — print PNG/JPEG/BMP with auto monochrome conversion
- QR codes — native ESC/POS QR commands
- Barcodes — UPC-A, UPC-E, EAN-13, EAN-8, Code 39, ITF, Codabar, Code 93, Code 128
- Horizontal lines — solid, dashed, dotted, double, star, equals
- Paper cut, cash drawer, beep
- Raw bytes pass-through for custom commands
Table of Contents
- Installation
- Android Setup
- iOS Setup
- Quick Start
- Finding Your Printer's Code Page
- Printing Text
- Text Styles
- Arabic Text
- Multi-Column Rows
- Horizontal Lines
- QR Codes
- Barcodes
- Images
- Receipt Templates
- Paper Cut, Cash Drawer & Beep
- Full API Reference
- Distribution Guide
1. Installation
Option A: Local Path (for development / private use)
Put the thermal_printer_billbay folder next to your app folder:
my_projects/
thermal_printer_billbay/ ← this plugin
my_app/ ← your Flutter app
In your app's pubspec.yaml:
dependencies:
thermal_printer_billbay:
path: ../thermal_printer_billbay
Option B: Git Repository (for sharing with team)
Push thermal_printer_billbay to a Git repo (GitHub, GitLab, Bitbucket), then:
dependencies:
thermal_printer_billbay:
git:
url: https://github.com/YOUR_USERNAME/thermal_printer_billbay.git
ref: main # or a specific tag like v1.0.0
Option C: Publish to pub.dev (for public distribution)
See Section 17: Distribution Guide.
After adding the dependency
Run in your app folder:
flutter pub get
No additional permission packages needed — the plugin handles Bluetooth permissions internally.
2. Android Setup
2.1 Permissions
Add these to your app's android/app/src/main/AndroidManifest.xml (inside the <manifest> tag, before <application>):
<!-- Bluetooth Classic permissions -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/>
<!-- Android 12+ Bluetooth permissions -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- Location (required for Bluetooth scanning on Android) -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
2.2 Request Permissions at Runtime
The plugin handles runtime permission requests automatically. Just call:
final printer = ThermalPrinterManager();
// Option A: One-call setup (handles permissions + Bluetooth enable)
final ready = await printer.ensureReady();
if (!ready) return; // permissions denied or Bluetooth off
// Option B: Step by step
final granted = await printer.requestPermissions();
if (!granted) return;
if (!await printer.isBluetoothEnabled()) {
await printer.enableBluetooth();
}
2.3 Min SDK
Make sure your android/app/build.gradle has minSdk at least 21:
android {
defaultConfig {
minSdk = 21
}
}
3. iOS Setup
3.1 Info.plist
Add these to ios/Runner/Info.plist:
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app needs Bluetooth to connect to thermal printers</string>
<key>UISupportedExternalAccessoryProtocols</key>
<array>
<string>com.yourprinter.protocol</string>
</array>
Replace com.yourprinter.protocol with your printer's MFi protocol string (check your printer's documentation).
3.2 Set Protocol in Code
final printer = ThermalPrinterManager();
await printer.setProtocol('com.yourprinter.protocol');
3.3 Important
iOS only supports MFi-certified printers via the ExternalAccessory framework. Regular Bluetooth printers that are not MFi certified will not work on iOS.
4. Quick Start
Here is the simplest possible example — setup, connect, print "Hello World", cut paper:
import 'package:thermal_printer_billbay/thermal_printer_billbay.dart';
// 1. Create the printer manager
final printer = ThermalPrinterManager();
// 2. Ensure permissions + Bluetooth are ready
final ready = await printer.ensureReady();
if (!ready) return;
// 3. Get already-paired devices
final devices = await printer.getBondedDevices();
print('Found ${devices.length} paired devices');
// 4. Connect to the first printer (with auto-retry)
final connected = await printer.connectWithRetry(devices.first.address);
if (!connected) {
print('Failed to connect');
return;
}
// 4. Build ESC/POS commands
final cmd = EscPosCommands(PaperSize.mm80);
cmd.initialize();
cmd.textLn('Hello World!', useLatin: true);
cmd.feed(3);
cmd.cut();
// 5. Send to printer
await printer.sendBytes(cmd.getBytes());
// 6. Disconnect when done
await printer.disconnect();
printer.dispose();
Scanning for New Devices
final printer = ThermalPrinterManager();
// Listen for discovered devices
printer.scanResults.listen((device) {
print('Found: ${device.name} (${device.address})');
});
// Listen for connection changes
printer.connectionState.listen((connected) {
print('Connected: $connected');
});
// Start scanning (also discovers nearby unpaired devices)
await printer.startScan();
// Stop after 10 seconds
Future.delayed(Duration(seconds: 10), () {
printer.stopScan();
});
5. Finding Your Printer's Code Page
This is the most important step for Arabic/special characters.
Every thermal printer has a list of code pages, but the numbers are different for every brand. The same encoding (e.g., Windows-1256 Arabic) might be number 46 on one printer and number 60 on another.
How to Find Your Code Page Number
-
Print a self-test page: Turn off your printer, then hold the FEED button and turn it on. It will print a page showing all supported code pages with their numbers.
-
Look for Arabic code pages: Find entries like:
46: WPC1256or46: Win-1256(Windows-1256 Arabic)22: PC864(IBM Arabic)41: PC864(same encoding, different number!)60: PC1001(another Arabic page)
-
Use the number from YOUR printer's self-test:
// If your printer shows "46: Win-1256" cmd.setCodePage(CodePage.win1256); // sends ESC t 46 // If your printer shows Arabic at number 41 cmd.setCodePageRaw(41); // sends ESC t 41 // For UTF-8 mode (Chinese-brand printers often support this) cmd.setCodePage(CodePage.utf8); // sends FS .
Built-in Code Page Presets
| Preset | Code Page | Number | Best For |
|---|---|---|---|
CodePage.utf8 |
PC10001 (UTF-8) | FS . | Chinese-brand printers, full Unicode |
CodePage.win1256 |
Win-1256 | 46 | Arabic (Epson standard) |
CodePage.cp864 |
CP864 | 22 | Arabic (IBM standard) |
CodePage.cp720 |
CP720 | 32 | Arabic DOS |
CodePage.pc437 |
PC437 | 0 | USA/Western Europe |
CodePage.win1252 |
WPC1252 | 16 | Western European |
CodePage.pc850 |
PC850 | 2 | Multilingual Latin |
Custom Code Page
If your printer uses a different number:
// Your printer has Arabic at position 60
cmd.setCodePageRaw(60);
// Or create a reusable CodePage object
final myCodePage = CodePage.customArabic(60);
cmd.setCodePage(myCodePage);
// Parse from user input (number or name)
cmd.setCodePageFromString('60'); // sends ESC t 60
cmd.setCodePageFromString('PC10001'); // sends FS . (UTF-8)
6. Printing Text
Basic Text
final cmd = EscPosCommands(PaperSize.mm80);
cmd.initialize();
cmd.setCodePage(CodePage.win1256);
// Print a line (adds line feed at end)
cmd.textLn('Hello World!', useLatin: true);
// Print text without line feed
cmd.text('Same line... ', useLatin: true);
cmd.textLn('continues here', useLatin: true);
// Print a blank line
cmd.newLine();
// Print Arabic text
cmd.textLn('مرحبا بالعالم');
cmd.feed(3);
await printer.sendBytes(cmd.getBytes());
The useLatin Parameter
useLatin: true— Use for English, numbers, symbols. Sends raw ASCII bytes (fast, always works).useLatin: false(default) — Use for Arabic or mixed text. Applies Arabic shaping and encodes using the selected code page.
7. Text Styles
Using textStyled() (recommended)
textStyled() applies styles, prints the text, then automatically resets styles back to normal:
cmd.initialize();
// Bold centered title
cmd.textStyled('MY STORE',
PosStyles(align: PrintAlign.center, bold: true, width: 2, height: 2),
useLatin: true);
// Underlined text
cmd.textStyled('Underlined', PosStyles(underline: true), useLatin: true);
// Double underline
cmd.textStyled('Double underline', PosStyles(doubleUnderline: true), useLatin: true);
// Reverse (white text on black background)
cmd.textStyled('Reversed!', PosStyles(reverse: true), useLatin: true);
// Font B (smaller font, more characters per line)
cmd.textStyled('Small font', PosStyles(fontType: FontType.fontB), useLatin: true);
// Wide text (2x width)
cmd.textStyled('Wide', PosStyles(width: 2), useLatin: true);
// Tall text (2x height)
cmd.textStyled('Tall', PosStyles(height: 2), useLatin: true);
// Combine multiple styles
cmd.textStyled('BIG BOLD',
PosStyles(bold: true, width: 2, height: 2, align: PrintAlign.center),
useLatin: true);
Using Manual Style Commands
For more control, set styles individually:
cmd.setBold(true);
cmd.setFontSize(2, 2); // width=2, height=2
cmd.setAlign(PrintAlign.center);
cmd.textLn('Custom styled', useLatin: true);
// Reset everything back to normal
cmd.resetStyles();
All Available Styles
| Style | PosStyles Property | Values |
|---|---|---|
| Alignment | align |
PrintAlign.left, .center, .right |
| Bold | bold |
true / false |
| Underline | underline |
true / false |
| Double underline | doubleUnderline |
true / false |
| Reverse | reverse |
true / false |
| Font | fontType |
FontType.fontA, .fontB |
| Width | width |
1 to 8 (multiplier) |
| Height | height |
1 to 8 (multiplier) |
| Upside down | upsideDown |
true / false |
8. Arabic Text
Basic Arabic Printing
cmd.initialize();
cmd.setCodePage(CodePage.win1256); // Use the code page that works for YOUR printer
cmd.textLn('مرحبا بالعالم'); // Hello World
cmd.textLn('اختبار الطباعة العربية'); // Arabic printing test
How Arabic Works
- Code page tells the printer which character encoding to use
- Arabic shaping converts letters to their correct joined forms (initial, medial, final, isolated)
- Bidi reversal flips the text so it prints right-to-left
The plugin handles steps 2 and 3 automatically when you use textLn() without useLatin: true.
Arabic with UTF-8 Mode
If your printer supports UTF-8 (FS . command), Arabic shaping uses Unicode Presentation Forms (U+FE70-U+FEFF) which gives the best results:
cmd.setCodePage(CodePage.utf8);
cmd.textLn('مرحبا بالعالم'); // Shaped + UTF-8 encoded
Arabic with Win-1256
If your printer uses Windows-1256 (common for Epson-compatible printers), the plugin encodes Arabic using a Win-1256 lookup table:
cmd.setCodePage(CodePage.win1256);
cmd.textLn('مرحبا بالعالم'); // Encoded as Win-1256 bytes
Mixed Arabic and English
cmd.textLn('الفاتورة Invoice'); // Mixed text
cmd.textLn('Hello مرحبا World');
9. Multi-Column Rows
Use row() to print tabular data. Column widths are proportional (like a 12-column grid):
// Header row
cmd.row([
PosColumn(text: 'Item', width: 6),
PosColumn(text: 'Qty', width: 2, align: PrintAlign.center),
PosColumn(text: 'Price', width: 4, align: PrintAlign.right),
], useLatin: true);
// Data rows
cmd.row([
PosColumn(text: 'Coffee', width: 6),
PosColumn(text: '2', width: 2, align: PrintAlign.center),
PosColumn(text: '5.00', width: 4, align: PrintAlign.right),
], useLatin: true);
Left-Right Alignment
For simple label-value pairs:
cmd.leftRight('Subtotal:', '45.00', useLatin: true);
cmd.leftRight('Tax (15%):', '6.75', useLatin: true);
cmd.leftRight('TOTAL:', '51.75', useLatin: true);
10. Horizontal Lines
cmd.hr(); // ------------------------------------------------
cmd.hr(style: LineStyle.solid); // ------------------------------------------------
cmd.hr(style: LineStyle.dashed); // - - - - - - - - - - - - - - - - - - - - - - - -
cmd.hr(style: LineStyle.dotted); // ................................................
cmd.hr(style: LineStyle.doubleLine); // ================================================
cmd.hr(style: LineStyle.star); // ************************************************
cmd.hr(style: LineStyle.equals); // ================================================
// Custom character
cmd.hr(customChar: '~'); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The line width is automatically calculated based on your paper size (48 chars for 80mm, 32 for 58mm, etc.).
11. QR Codes
cmd.printQrCode(
'https://example.com',
size: 6, // Module size: 1-16 (default: 6)
errorLevel: QrErrorLevel.medium, // L, M, Q, H (default: M)
align: PrintAlign.center, // Alignment (default: center)
);
QR Error Correction Levels
| Level | Recovery | Best For |
|---|---|---|
QrErrorLevel.low |
~7% | Short URLs, clean environment |
QrErrorLevel.medium |
~15% | General use (default) |
QrErrorLevel.quartile |
~25% | Receipts that may get crumpled |
QrErrorLevel.high |
~30% | Harsh environments |
12. Barcodes
cmd.printBarcode(
'1234567890128',
BarcodeType.ean13,
height: 80, // Barcode height in dots (default: 80)
width: 2, // Bar width: 2-6 (default: 2)
textPosition: 2, // 0=none, 1=above, 2=below, 3=both
align: PrintAlign.center,
);
Supported Barcode Types
| Type | Data Format |
|---|---|
BarcodeType.upcA |
11-12 digits |
BarcodeType.upcE |
6-8 digits |
BarcodeType.ean13 |
12-13 digits |
BarcodeType.ean8 |
7-8 digits |
BarcodeType.code39 |
A-Z, 0-9, space, $%+-./ |
BarcodeType.itf |
Even number of digits |
BarcodeType.codabar |
0-9, A-D, $+-./: |
BarcodeType.code93 |
ASCII 0-127 |
BarcodeType.code128 |
ASCII 0-127 (most flexible) |
13. Images
Print any image (PNG, JPEG, BMP). It's automatically converted to monochrome and scaled to fit the paper width:
import 'dart:io';
// Load image from file
final imageBytes = await File('path/to/logo.png').readAsBytes();
cmd.printImage(
imageBytes,
maxWidth: 384, // Max width in dots (optional, defaults to paper width)
threshold: 127, // Black/white threshold: 0-255 (default: 127)
align: PrintAlign.center,
);
From Flutter Assets
import 'package:flutter/services.dart';
final data = await rootBundle.load('assets/logo.png');
final imageBytes = data.buffer.asUint8List();
cmd.printImage(imageBytes);
14. Receipt Templates
Full Receipt
final cmd = EscPosCommands(PaperSize.mm80);
cmd.setCodePage(CodePage.win1256);
final receipt = ReceiptTemplate(
cmd: cmd,
storeName: 'MY STORE',
storeAddress: '123 Main Street',
storePhone: '555-1234',
storeEmail: 'info@mystore.com',
currency: '\$', // Currency symbol before prices
);
receipt.build(
items: [
ReceiptItem(name: 'Coffee', quantity: 2, price: 5.00),
ReceiptItem(name: 'Sandwich', quantity: 1, price: 8.50),
ReceiptItem(name: 'Cookie', quantity: 3, price: 2.00, discount: 1.00),
],
taxRate: 0.15, // 15% tax
discount: 2.0, // Overall discount amount
receiptNumber: '001234',
cashierName: 'Ahmad',
customerName: 'John',
footerText: 'Thank you for your purchase!',
footerTextArabic: 'شكرا لزيارتكم',
qrData: 'https://mystore.com/receipt/001234',
barcodeData: '001234',
cutAfter: true, // Auto cut paper after receipt
);
await printer.sendBytes(cmd.getBytes());
This prints:
MY STORE
123 Main Street
Tel: 555-1234
info@mystore.com
------------------------------------------------
Receipt #: 001234
Date: 2026-02-07 14:30
Cashier: Ahmad
Customer: John
------------------------------------------------
Item Qty Price Total
- - - - - - - - - - - - - - -
Coffee 2 5.00 10.00
Sandwich 1 8.50 8.50
Cookie 3 2.00 5.00
Disc: -1.00
- - - - - - - - - - - - - - -
Subtotal: 23.50
Discount: -2.00
Tax (15%): 3.23
- - - - - - - - - - - - - - -
TOTAL: 24.73
------------------------------------------------
Thank you for your purchase!
شكرا لزيارتكم
[QR CODE]
||||| BARCODE |||||
001234
Simple Receipt (minimal)
For a quick receipt without store info:
final cmd = EscPosCommands(PaperSize.mm80);
cmd.initialize();
SimpleReceipt.print(
cmd,
title: 'RECEIPT',
items: [
ReceiptItem(name: 'Item A', quantity: 2, price: 10.0),
ReceiptItem(name: 'Item B', quantity: 1, price: 25.0),
],
cutAfter: true,
);
await printer.sendBytes(cmd.getBytes());
Custom Receipt (build your own)
You can build any layout using the low-level commands:
final cmd = EscPosCommands(PaperSize.mm80);
cmd.initialize();
cmd.setCodePage(CodePage.win1256);
// Header
cmd.textStyled('INVOICE',
PosStyles(align: PrintAlign.center, bold: true, width: 2, height: 2),
useLatin: true);
cmd.textStyled('My Company LLC',
PosStyles(align: PrintAlign.center),
useLatin: true);
cmd.hr(style: LineStyle.doubleLine);
// Items
cmd.row([
PosColumn(text: 'Description', width: 6),
PosColumn(text: 'Qty', width: 2, align: PrintAlign.center),
PosColumn(text: 'Amount', width: 4, align: PrintAlign.right),
], useLatin: true);
cmd.hr(style: LineStyle.dashed);
cmd.row([
PosColumn(text: 'Laptop', width: 6),
PosColumn(text: '1', width: 2, align: PrintAlign.center),
PosColumn(text: '999.00', width: 4, align: PrintAlign.right),
], useLatin: true);
cmd.hr(style: LineStyle.doubleLine);
// Total
cmd.setBold(true);
cmd.setFontSize(2, 1);
cmd.leftRight('TOTAL:', '\$999.00', useLatin: true);
cmd.resetStyles();
// Arabic footer
cmd.setAlign(PrintAlign.center);
cmd.textLn('شكرا لك');
cmd.setAlign(PrintAlign.left);
// QR code
cmd.printQrCode('https://mycompany.com/inv/12345', size: 5);
cmd.feedAndCut();
await printer.sendBytes(cmd.getBytes());
15. Paper Cut, Cash Drawer & Beep
Paper Cut
cmd.cut(); // Full cut
cmd.cut(mode: CutMode.partial); // Partial cut (leaves a small strip)
cmd.feedAndCut(); // Feed 3 lines + full cut
cmd.feedAndCut(lines: 5, mode: CutMode.partial); // Custom
Cash Drawer
cmd.openDrawer(); // Open via pin 2 (default)
cmd.openDrawer(pin: DrawerPin.pin5); // Open via pin 5
Beep
cmd.beep(); // Single beep
cmd.beep(count: 3, duration: 5); // 3 beeps, each 5 units long
Paper Feed
cmd.feed(1); // Feed 1 line
cmd.feed(5); // Feed 5 lines
cmd.newLine(); // Single line feed
16. Full API Reference
ThermalPrinterManager
| Method | Description |
|---|---|
ensureReady() |
One-call setup: permissions + Bluetooth enable |
requestPermissions() |
Request Bluetooth permissions (shows system dialog) |
checkPermissions() |
Check which permissions are granted |
isBluetoothEnabled() |
Check if Bluetooth is turned on |
enableBluetooth() |
Show system dialog to enable Bluetooth |
getBondedDevices() |
Get list of already-paired Bluetooth devices |
startScan() |
Start discovering nearby Bluetooth devices |
stopScan() |
Stop scanning |
connect(address) |
Connect to a printer by MAC address |
connectWithRetry(address) |
Connect with automatic retry on failure |
disconnect() |
Disconnect from current printer |
sendBytes(Uint8List) |
Send raw bytes to the printer |
checkConnected() |
Check if still connected |
setProtocol(String) |
Set MFi protocol string (iOS only) |
scanResults |
Stream of discovered devices |
connectionState |
Stream of connection state changes |
isConnected |
Current connection status |
dispose() |
Release resources |
EscPosCommands
| Method | Description |
|---|---|
initialize() |
Reset printer to defaults (ESC @) |
resetStyles() |
Reset text formatting only |
setCodePage(CodePage) |
Set code page from preset |
setCodePageRaw(int) |
Set code page by raw number |
setCodePageFromString(String) |
Parse code page from string |
setCharSet(CharSet) |
Set international character set |
setBold(bool) |
Bold on/off |
setUnderline(int) |
0=off, 1=single, 2=double |
setFontSize(w, h) |
Size multiplier 1-8 |
setAlign(PrintAlign) |
Left, center, right |
setFont(FontType) |
Font A or B |
setReverse(bool) |
White on black |
setUpsideDown(bool) |
Upside down text |
setLineSpacing(int) |
Line spacing in dots |
resetLineSpacing() |
Reset to default spacing |
setCharSpacing(int) |
Character spacing in dots |
setDensity(int) |
Print darkness 0-15 |
text(String) |
Print text (no line feed) |
textLn(String) |
Print text + line feed |
textStyled(String, PosStyles) |
Print with styles (auto-resets) |
newLine() |
Print blank line |
writeRaw(String) |
Write raw code units |
rawBytes(Uint8List) |
Write raw bytes |
row(List<PosColumn>) |
Multi-column row |
leftRight(left, right) |
Left-right aligned text |
hr() |
Horizontal line |
feed(int) |
Feed n lines |
cut() |
Cut paper |
feedAndCut() |
Feed + cut |
openDrawer() |
Open cash drawer |
beep() |
Buzzer beep |
printImage(Uint8List) |
Print image |
printRasterImage(...) |
Print pre-processed image |
printQrCode(String) |
Print QR code |
printBarcode(String, BarcodeType) |
Print barcode |
getBytes() |
Get buffered bytes + clear |
peekBytes() |
Get bytes without clearing |
clear() |
Clear buffer |
PaperSize
| Size | Font A Chars | Font B Chars | Max Dot Width |
|---|---|---|---|
PaperSize.mm58 |
32 | 42 | 384 |
PaperSize.mm72 |
42 | 56 | 512 |
PaperSize.mm80 |
48 | 64 | 576 |
PaperSize.mm110 |
66 | 88 | 832 |
17. Distribution Guide
How to Share This Package
Option 1: Share the Folder (simplest)
- Copy the
thermal_printer_billbay/folder to anyone who needs it - They put it next to their app folder
- They add to their
pubspec.yaml:dependencies: thermal_printer_billbay: path: ../thermal_printer_billbay - Run
flutter pub get
Option 2: Push to GitHub (recommended for teams)
Step 1: Create a GitHub repo
- Go to https://github.com/new
- Name it
thermal_printer_billbay - Set it to Public or Private
- Do NOT add a README (we already have one)
Step 2: Push your code
Open a terminal in the thermal_printer_billbay/ folder:
cd thermal_printer_billbay
git init
git add .
git commit -m "Initial release v1.0.0"
git branch -M main
git remote add origin https://github.com/YOUR_USERNAME/thermal_printer_billbay.git
git push -u origin main
Step 3: Create a version tag (optional but recommended)
git tag v1.0.0
git push origin v1.0.0
Step 4: Others can now use it
# In their pubspec.yaml
dependencies:
thermal_printer_billbay:
git:
url: https://github.com/YOUR_USERNAME/thermal_printer_billbay.git
ref: v1.0.0 # or 'main' for latest
Option 3: Publish to pub.dev (public packages)
Step 1: Prepare the package
- Open
thermal_printer_billbay/pubspec.yaml - Remove or change the
publish_to: 'none'line - Make sure
name,description, andversionare set - Make sure this README.md exists and is complete
Step 2: Check the package
cd thermal_printer_billbay
dart pub publish --dry-run
Fix any issues it reports.
Step 3: Create a Google account (if you don't have one)
You need a Google account to publish to pub.dev.
Step 4: Publish
dart pub publish
This will open a browser for Google sign-in, then upload the package.
Step 5: Others can now use it
# In their pubspec.yaml
dependencies:
thermal_printer_billbay: ^1.0.0
Troubleshooting
"Arabic prints as Chinese/garbage characters"
Your printer is using the wrong code page. Print a self-test page (hold FEED button while powering on) and find the Arabic code page number. Then use that number:
cmd.setCodePageRaw(YOUR_NUMBER_HERE);
"Cannot find printer"
- Make sure Bluetooth is turned on
- Make sure the printer is powered on and in pairing mode
- On Android, grant all Bluetooth and Location permissions
- Try pairing the printer first in your phone's Bluetooth settings
"Connection failed"
- Make sure no other app is connected to the printer
- Turn the printer off and on
- Remove the pairing and re-pair in Bluetooth settings
"Arabic letters are not joined"
The plugin automatically shapes Arabic text. Make sure you're NOT using useLatin: true for Arabic text:
// WRONG — Arabic won't be shaped
cmd.textLn('مرحبا', useLatin: true);
// CORRECT — Arabic will be shaped automatically
cmd.textLn('مرحبا');
"Image is too dark/light"
Adjust the threshold value (0=all black, 255=all white):
cmd.printImage(imageBytes, threshold: 100); // Darker
cmd.printImage(imageBytes, threshold: 180); // Lighter
License
This package is provided as-is for free use. Modify and distribute as needed.
Libraries
- thermal_printer_billbay
- A comprehensive Flutter plugin for ESC/POS thermal printers via Bluetooth Classic (SPP).