srag_printer 0.2.0
srag_printer: ^0.2.0 copied to clipboard
Flexible Flutter thermal printing package for PDF widgets, PDFs, and image frames over network, USB, and Bluetooth.
srag_printer
A flexible Flutter thermal printing package for printing PDF widgets, PDF bytes, and image frames through ESC/POS thermal printers.
It is intentionally generic. It does not know about your POS models, tax rules, ZATCA QR generation, preferences, providers, or app assets. Your app builds the content; srag_printer renders and prints it.
Screenshots #
| English Receipt | Arabic Receipt (RTL) | Bilingual Receipt |
|---|---|---|
| LTR layout | Full RTL support | English + Arabic side by side |
Acknowledgements #
srag_printer is built on top of excellent Flutter/Dart packages, including:
pdffor building PDF documents with Dart widgets.pdfxfor rendering PDF pages into images.imagefor image decoding and processing.esc_pos_utils_plusfor ESC/POS command generation utilities.usb_serialfor Android USB serial communication (through the optionalsrag_printer_usbpackage).flutter_blue_plusfor Bluetooth Low Energy discovery and communication.- Android's
BluetoothSocket(through this plugin's own Java code) for Classic Bluetooth SPP printing. ffifor Windows RAW driver and serial port bindings.
Many thanks to the maintainers and contributors of these packages.
What This Package Does #
- Prints any
pw.Widgetfrompackage:pdf/widgets.dart. - Prints existing PDF bytes.
- Prints existing image frames as
List<Uint8List>. - Converts PDF pages into thermal-printer-width image frames.
- Converts images to ESC/POS raster commands.
- Sends bytes through network, USB, Bluetooth BLE, Windows driver, or Windows serial transports.
- Provides generic receipt widgets as optional helpers.
- Provides generic device discovery by type/platform.
Installation #
Add the package to your app:
dependencies:
srag_printer: ^0.2.0
Then import:
import 'package:srag_printer/srag_printer.dart';
import 'package:pdf/widgets.dart' as pw;
Android USB printing (optional) #
Android USB support lives in the separate srag_printer_usb package, so apps
that only print over the network or Bluetooth do not pull in a USB plugin. To
print over USB on Android, add it and register it once at startup:
dependencies:
srag_printer: ^0.2.0
srag_printer_usb: ^0.1.0
import 'package:srag_printer_usb/srag_printer_usb.dart';
void main() {
SragPrinterUsb.register();
runApp(const MyApp());
}
Without it, Android USB discovery returns no devices and creating a USB
transport throws PrinterUnsupportedPlatformException. USB on Windows goes
through the built-in COM serial and driver transports and needs nothing extra.
Platform Setup and Permissions #
The package sends bytes to printers, but each application owns its platform permissions. The example app includes these settings as a reference.
Android #
Add the permissions you need to android/app/src/main/AndroidManifest.xml.
Keep hardware features optional if your app can still use network printing when
USB or Bluetooth hardware is unavailable.
<!-- Network printers, usually raw TCP on port 9100. -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- USB host mode for Android USB printers. -->
<uses-feature android:name="android.hardware.usb.host" android:required="false" />
<!-- Bluetooth for Android 11 and below. Location is required by Android for BLE scanning. -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" />
<!-- Bluetooth for Android 12+. -->
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-feature android:name="android.hardware.bluetooth" android:required="false" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false" />
Runtime permissions are still required before Bluetooth discovery. The example
uses permission_handler to request bluetoothScan, bluetoothConnect, and
locationWhenInUse on Android.
USB note: Android USB access is device-selection dependent. The OS may show a USB permission prompt when a USB transport opens the selected device.
iOS #
For network printers on the local network and BLE discovery/connect, add usage
descriptions to ios/Runner/Info.plist:
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to discover and connect to BLE thermal printers.</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app uses Bluetooth to discover and connect to BLE thermal printers.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>This app connects to network thermal printers on your local network.</string>
iOS does not support generic USB thermal printer access in v1. Classic Bluetooth SPP is also not available for generic printers; use network or BLE printers that expose writable BLE characteristics.
macOS #
If your macOS app uses the app sandbox, add outbound network and Bluetooth entitlements. The example adds these to both debug/profile and release entitlements:
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.device.bluetooth</key>
<true/>
Windows #
No manifest permissions are required for raw TCP sockets, installed Windows printer RAW output, or COM-port access. Users may still need normal OS-level access to the selected printer/port, and firewall rules can affect network printers.
Linux #
Network printing can work with normal socket access. USB/serial access usually
depends on system groups and udev rules such as dialout or lp; configure
those at the OS level for your target device.
Platform Support #
| Capability | Windows | Android | iOS |
|---|---|---|---|
Print pw.Widget |
Yes | Yes | Yes |
| Print PDF bytes | Yes | Yes | Yes |
| Print image frames | Yes | Yes | Yes |
| Network TCP/IP | Yes | Yes | Yes |
| USB direct | Yes via serial/driver | Yes, with srag_printer_usb |
Not in v1 |
| Windows driver RAW | Yes | No | No |
| Windows COM serial | Yes | No | No |
| Bluetooth classic (SPP) | Not in v1 | Yes, paired devices | No (iOS does not allow it) |
| BLE Bluetooth | Yes | Possible | Possible |
| Generic USB discovery | COM/system devices | USB devices, with srag_printer_usb |
No |
| Network discovery | Manual IP/port | Manual IP/port | Manual IP/port |
Quick Start: Network Printer #
final printer = SragPrinter(
transport: NetworkPrinterTransport(host: '192.168.1.50', port: 9100),
config: const SragPrinterConfig(
paper: SragPaper.mm80(headDots: 528),
),
);
await printer.printWidget(
(_) => pw.Column(
children: [
pw.Text('Hello from srag_printer'),
pw.Text('Printed from a PDF widget'),
],
),
);
Print Any PDF Widget #
Use this when your app wants total control over layout:
await printer.printWidget(
(_) => pw.Directionality(
textDirection: pw.TextDirection.rtl,
child: pw.Column(
children: [
pw.Text('فاتورة تجريبية'),
pw.Divider(),
pw.Text('You can render any pdf widget here.'),
],
),
),
);
Print PDF Bytes #
final pdfBytes = await buildMyPdfSomewhereElse();
await printer.printPdf(pdfBytes);
Print Image Frames #
final frames = <Uint8List>[pngFrame1, pngFrame2];
await printer.printImages(frames);
Receipt Widgets #
The receipt widgets are optional helpers. You can use them, mix them with your own pw.Widgets, or ignore them completely.
await printer.printWidget(
(_) => SragReceipt(
textDirection: pw.TextDirection.ltr,
children: [
ReceiptHeader(
titles: ['Simplified Tax Invoice', 'فاتورة ضريبية مبسطة'],
infoRows: const [
ReceiptKeyValue(label: 'Invoice No', value: '10001'),
ReceiptKeyValue(label: 'Date', value: '2026-06-27'),
],
),
ReceiptTable<Map<String, String>>(
columns: [
ReceiptTableColumn(title: 'Product', flex: 4, valueBuilder: (x) => x['name']!),
ReceiptTableColumn(title: 'Qty', valueBuilder: (x) => x['qty']!),
ReceiptTableColumn(title: 'Total', valueBuilder: (x) => x['total']!),
],
rows: const [
{'name': 'Coffee', 'qty': '1', 'total': '12.00'},
],
),
ReceiptTotals(
rows: const [
ReceiptKeyValue(label: 'Subtotal', value: '12.00'),
ReceiptKeyValue(label: 'Tax', value: '1.80'),
ReceiptKeyValue(label: 'Total', value: '13.80'),
],
),
ReceiptFooter(lines: const ['Thank you']),
],
),
);
Arabic / RTL and Custom Fonts #
srag_printer ships with Almarai (Regular + Bold) as bundled package assets. These fonts support both Latin and Arabic glyphs out of the box.
Loading the default fonts #
Call SragFonts.load() once at app startup (or before the first print). After that, SragReceiptStyle.defaults() automatically uses the bundled fonts:
import 'package:srag_printer/srag_printer.dart';
// Call once, e.g. in initState or main:
await SragFonts.load();
// SragReceiptStyle.defaults() now uses Almarai automatically:
final style = SragReceiptStyle.defaults();
Creating a style with the loaded fonts #
final style = SragReceiptStyle.defaults(
font: almaraiRegular,
boldFont: almaraiBold,
);
Arabic receipt (RTL) #
await printer.printWidget(
(_) => SragReceipt(
style: style,
textDirection: pw.TextDirection.rtl,
children: [
ReceiptHeader(
style: style,
titles: const ['مقهى البيت', 'فاتورة ضريبية'],
infoRows: const [
ReceiptKeyValue(label: 'رقم الفاتورة', value: '10002'),
ReceiptKeyValue(label: 'التاريخ', value: '2026-06-27'),
],
),
ReceiptTable<Map<String, String>>(
style: style,
columns: [
ReceiptTableColumn(title: 'المنتج', flex: 2, valueBuilder: (x) => x['name']!),
ReceiptTableColumn(title: 'الكمية', valueBuilder: (x) => x['qty']!),
ReceiptTableColumn(title: 'الإجمالي', valueBuilder: (x) => x['total']!),
],
rows: const [
{'name': 'قهوة لاتيه', 'qty': '2', 'total': '24.00'},
],
),
ReceiptTotals(
style: style,
rows: const [
ReceiptKeyValue(label: 'المجموع', value: '24.00'),
ReceiptKeyValue(label: 'الضريبة', value: '2.40'),
ReceiptKeyValue(label: 'الإجمالي', value: '26.40'),
],
),
ReceiptFooter(style: style, lines: const ['شكراً لزيارتكم!']),
],
),
);
Bilingual receipt (English + Arabic) #
Use trailingLabel on ReceiptKeyValue to display both languages side by side:
await printer.printWidget(
(_) => SragReceipt(
style: style,
children: [
ReceiptHeader(
style: style,
titles: const ['Coffee House | مقهى البيت'],
infoRows: const [
ReceiptKeyValue(label: 'Invoice', value: '10003', trailingLabel: 'فاتورة'),
ReceiptKeyValue(label: 'Date', value: '2026-06-27', trailingLabel: 'التاريخ'),
],
),
// ... table, totals, footer with style: style
],
),
);
Using your own fonts #
You are not limited to the bundled Almarai font. Load any .ttf file and pass it to SragReceiptStyle.defaults() to override the defaults:
import 'package:flutter/services.dart' show rootBundle;
import 'package:pdf/widgets.dart' as pw;
final myFont = pw.Font.ttf(await rootBundle.load('assets/fonts/MyFont-Regular.ttf'));
final myBold = pw.Font.ttf(await rootBundle.load('assets/fonts/MyFont-Bold.ttf'));
final style = SragReceiptStyle.defaults(font: myFont, boldFont: myBold);
Custom Table Rows #
ReceiptTable<MyItem>(
columns: columns,
rows: items,
rowBuilder: (context, item, columns) {
return pw.Column(
children: [
pw.Text(item.name),
pw.Text('Custom options, modifiers, or notes'),
],
);
},
);
QR Codes #
srag_printer does not generate QR content. Generate the QR in your app, then pass it as an image or widget:
ReceiptQrSection.image(qrPngBytes);
ReceiptQrSection.widget(myQrPdfWidget);
Discovery #
Discovery is generic and platform-aware. It does not hardcode printer brands or choose for you:
final devices = await SragPrinterDiscovery.discover(
request: const PrinterDiscoveryRequest(
connectionType: PrinterConnectionType.usb,
),
);
final selected = devices.first; // Your UI or app policy chooses this.
final transport = selected.createTransport();
Optional filters are app-controlled:
final devices = await SragPrinterDiscovery.discover(
request: const PrinterDiscoveryRequest(
connectionType: PrinterConnectionType.usb,
filters: {'nameContains': 'printer'},
),
);
Classic Bluetooth (Android) #
On Android, Bluetooth discovery lists every paired device, Classic and BLE,
with its type in device.metadata['bluetoothType'] (a BluetoothDeviceType:
classic, le, dual, or unknown). createTransport() then picks the
transport:
- Paired
classic,dual, andunknowndevices print over Classic Bluetooth SPP throughClassicBluetoothPrinterTransport. This covers the built-in printers of POS terminals, which appear as a paired virtual device (oftenInnerPrinterat00:11:22:33:44:55) that reports no type. ledevices, and devices found by the BLE scan, print over BLE throughBluetoothPrinterTransport.
Pair Classic printers in the Android Bluetooth settings first; discovery does
not scan for unpaired Classic devices. Android 12+ needs the
BLUETOOTH_CONNECT runtime permission to list paired devices and connect.
Paper and Print Options #
const config = SragPrinterConfig(
paper: SragPaper.mm58(headDots: 384),
chunkSize: 512,
chunkDelay: Duration(milliseconds: 6),
feedLines: 3,
cutAfterPrint: true,
);
Use SragPaper.custom(widthMm: ..., headDots: ...) for uncommon printers.
Troubleshooting #
Cannot reach printer at host:port: check IP, port, network, and firewall.No writable BLE characteristic found: the printer may use a proprietary BLE service. Classic SPP printers are printed over SPP automatically on Android once they are paired.Failed to open Classic Bluetooth printer: make sure the printer is on, paired in Android Bluetooth settings, and not connected to another phone.USB transport is supported on Android: use Windows driver/serial transports on Windows.Image frame could not be decoded: pass PNG/JPEG bytes, not raw pixels.- Cut command ignored: some printers do not have a cutter, or the driver may block raw ESC/POS commands.
Migration Guide #
Keep app-specific logic in your app:
SalesInvoice / app model -> your adapter -> pw.Widget or receipt data -> srag_printer
Do not move tax, QR generation, ZATCA logic, local preferences, or app assets into this package.
Migrating from 0.1.x to 0.2.0 #
UsbPrinterTransportmoved tosrag_printer_usb. If you print over USB on Android, add that package, callSragPrinterUsb.register()at startup, and importUsbPrinterTransportfrompackage:srag_printer_usb/srag_printer_usb.dart.- Nothing changes for network, Bluetooth, or Windows printing.
- Bluetooth discovery now skips paired devices outside Android instead of
throwing on iOS, and waits for the adapter to leave the
unknownstate before scanning.
FAQ #
Can I print any Flutter widget?
No. The input must be a pdf/widgets.dart widget (pw.Widget), not a normal Flutter Widget.
Can the package discover network printers automatically?
Not in v1. Use manual IP/port for reliability and predictable behavior.
Does iOS support USB printing?
Generic USB thermal printing is not supported in v1.
Does the package generate QR codes?
No. Pass a ready QR image or pw.Widget.