') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - CodingDevs/thermal_printer · GitHub
Skip to content

Repository files navigation

thermal_printer

Pub Version

A library to discover printers, and send printer commands.

This library allows to print esc commands to printers in different platforms such as android, ios, windows and different interfaces as Bluetooth and BLE, USB and Wifi/Ethernet

Inspired by flutter_pos_printer.

Main Features

  • Android, iOS and Windows support
  • Scan for bluetooth devices
  • Send raw List<int> bytes data to a device, review this library to generate ESC/POS commands flutter_esc_pos_utils.

Features

AndroidiOSWindowsDescription
USB interface🔳Allows connection with usb devices.
Bluetooth classic interface🔳🔳Allows connection with classic bt devices.
Bluetooth low energy (BLE) interface🔳Allows connection with bt BLE devices.
Net (ethernet/wifi) interfaceAllows connection with network devices.
scanStarts a scan for only Bluetooth devices or network devices(Android/iOS).
connectEstablishes a connection to the device.
disconnectCancels an active or pending connection to the device.
stateStream of state changes for the Bluetooth Device.
printprint bytes.

Getting Started

For a full example please check /example folder. Here are only the most important parts of the code to illustrate how to use the library.

Generate bytes to print through flutter_esc_pos_utils.

 import 'package:esc_pos_utils/esc_pos_utils.dart';
final profile =awaitCapabilityProfile.load();
final generator =Generator(PaperSize.mm58, profile);
List<int> bytes = [];
bytes += generator.text('Test Print', styles:constPosStyles(align:PosAlign.center));
bytes += generator.text('Product 1');
bytes += generator.text('Product 2');

Android

Allow to connect bluetooth (classic and BLE), USB and network devices

Change the minSdkVersion for Android

thermal_printer is compatible only from version 21 of Android SDK so you should change this in android/app/build.gradle:

In build.gradle set

 defaultConfig {
...
minSdkVersion 19
targetSdkVersion 33
...

select type of device PrinterType ( bluetooth, usb, network)

if select bluetooth you can send optional params

  • isBle -> allow to connect with bluetooth that supports this technology
  • autoconnect -> allow to reconnect when state of device is None

USB: you can enable the native broadcast receiver to notify connected usb devices put the following code in AndroidManifest

 <receiver
android:name="com.codingdevs.thermal_printer.usb.UsbReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.hardware.usb.action.ACTION_USB_PERMISSION" />
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
</receiver>

iOS

Allow to connect bluetooth (BLE) and network devices

Windows

Allow to connect USB and network devices To network devices is necessary to set ipAddress

How to use it

init a PrinterManager instance

import'package:thermal_printer/thermal_printer.dart';
var printerManager =PrinterManager.instance;

scan

var devices = [];
_scan(PrinterType type, {bool isBle =false}) {
// Find printersPrinterManager.instance.discovery(type: type, isBle: isBle).listen((device) {
devices.add(device);
});
}

connect

_connectDevice(PrinterDevice selectedPrinter, PrinterType type, {bool reconnect =false, bool isBle =false, String? ipAddress =null}) async {
switch (type) {
// only windows and androidcasePrinterType.usb:awaitPrinterManager.instance.connect(
type: type,
model:UsbPrinterInput(name: selectedPrinter.name, productId: selectedPrinter.productId, vendorId: selectedPrinter.vendorId));
break;
// only iOS and androidcasePrinterType.bluetooth:awaitPrinterManager.instance.connect(
type: type,
model:BluetoothPrinterInput(
name: selectedPrinter.name,
address: selectedPrinter.address!,
isBle: isBle,
autoConnect: reconnect));
break;
casePrinterType.network:awaitPrinterManager.instance.connect(type: type, model:TcpPrinterInput(ipAddress: ipAddress ?? selectedPrinter.address!));
break;
default:
}
}

disconnect

_disconnectDevice(PrinterType type) async {
awaitPrinterManager.instance.disconnect(type: type);
}

listen bluetooth state

PrinterManager.instance.stateBluetooth.listen((status) {
log(' ----------------- status bt $status ------------------ ');
});

send bytes to print

_sendBytesToPrint(List<int> bytes, PrinterType type) async { PrinterManager.instance.send(type: type, bytes: bytes);
}

Troubleshooting

error:'State restoration of CBCentralManager is only allowed for applications that have specified the "bluetooth-central" background mode' info.plist add:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>Allow App use bluetooth?</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Allow App use bluetooth?</string>
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
<string>bluetooth-peripheral</string>
</array>

Credits

About

No description, website, or topics provided.

Resources

Stars

20 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages