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.flutter_blue_plusfor Bluetooth Low Energy discovery and communication.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. During local development inside this repository:
dependencies:
srag_printer:
path: packages/srag_printer
Then import:
import 'package:srag_printer/srag_printer.dart';
import 'package:pdf/widgets.dart' as pw;
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 | Not in v1 |
| Windows driver RAW | Yes | No | No |
| Windows COM serial | Yes | No | No |
| Bluetooth classic | Not in v1 | Not in v1 | Not in v1 |
| BLE Bluetooth | Yes | Possible | Possible |
| Generic USB discovery | COM/system devices | USB devices | 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'},
),
);
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 classic Bluetooth SPP or a proprietary BLE service.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.
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.
Libraries
- srag_printer
- Flexible Flutter thermal printing tools for PDF widgets, PDF bytes, and raster image frames.