Flutter热敏打印机控制插件esc_pos_printer_plus的使用
Flutter热敏打印机控制插件esc_pos_printer_plus的使用
该库允许使用ESC/POS热敏WiFi/以太网打印机打印收据。对于蓝牙打印机,请使用esc_pos_bluetooth
库。
该库可用于Flutter或纯Dart项目。对于Flutter项目,支持Android和iOS平台。
要扫描网络中的打印机,可以考虑使用ping_discover_network
包。请注意,大多数ESC/POS打印机默认监听9100端口。
待办事项(欢迎提交PR!)
- 使用
GS ( k
命令打印QR码(已支持从图像打印QR码) - 使用
GS ( k
命令打印PDF-417条形码 - 使用
ESC 3 <n>
命令设置行间距
如何帮助
- 测试您的打印机并将其添加到表格中:WiFi/网络打印机 或 蓝牙打印机
- 测试并报告错误
- 分享您的改进建议(代码优化、新功能等)
- 欢迎提交PR!
已测试打印机
以下是一些使用此库测试过的打印机。请添加您已测试的型号,以维护和改进此库,并帮助其他人选择合适的打印机。
生成收据
简单收据与样式
void testReceipt(NetworkPrinter printer) {
printer.text(
'Regular: aA bB cC dD eE fF gG hH iI jJ kK lL mM nN oO pP qQ rR sS tT uU vV wW xX yY zZ');
printer.text('Special 1: àÀ èÈ éÉ ûÛ üÜ çÇ ôÔ',
styles: PosStyles(codeTable: 'CP1252'));
printer.text('Special 2: blåbærgrød',
styles: PosStyles(codeTable: 'CP1252'));
printer.text('Bold text', styles: PosStyles(bold: true));
printer.text('Reverse text', styles: PosStyles(reverse: true));
printer.text('Underlined text',
styles: PosStyles(underline: true), linesAfter: 1);
printer.text('Align left', styles: PosStyles(align: PosAlign.left));
printer.text('Align center', styles: PosStyles(align: PosAlign.center));
printer.text('Align right',
styles: PosStyles(align: PosAlign.right), linesAfter: 1);
printer.text('Text size 200%',
styles: PosStyles(
height: PosTextSize.size2,
width: PosTextSize.size2,
));
printer.feed(2);
printer.cut();
}
您可以在这里找到更多示例:esc_pos_utils。
打印收据
import 'package:esc_pos_printer_plus/esc_pos_printer_plus.dart';
const PaperSize paper = PaperSize.mm80;
final profile = await CapabilityProfile.load();
final printer = NetworkPrinter(paper, profile);
final PosPrintResult res = await printer.connect('192.168.0.123', port: 9100);
if (res == PosPrintResult.success) {
testReceipt(printer);
printer.disconnect();
}
print('Print result: ${res.msg}');
完整的示例可以在example/example.dart
和example/discover_printers
中查看。
测试打印
完整示例代码
import 'dart:developer';
import 'package:esc_pos_printer_plus/esc_pos_printer_plus.dart';
import 'package:esc_pos_utils_plus/esc_pos_utils_plus.dart';
import 'package:flutter/material.dart' hide Image;
import 'package:flutter/services.dart';
import 'package:image/image.dart';
import 'package:intl/intl.dart';
import 'package:ping_discover_network_plus/ping_discover_network_plus.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Discover Printers',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
[@override](/user/override)
MyHomePageState createState() => MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
String localIp = '';
List<String> devices = [];
bool isDiscovering = false;
int found = -1;
TextEditingController portController = TextEditingController(text: '9100');
void discover(BuildContext ctx) async {
setState(() {
isDiscovering = true;
devices.clear();
found = -1;
});
String ip;
try {
ip = '192.168.5.111';
log('local ip:\t$ip');
} catch (e) {
const snackBar = SnackBar(
content: Text('WiFi is not connected', textAlign: TextAlign.center));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
return;
}
setState(() {
localIp = ip;
});
final String subnet = ip.substring(0, ip.lastIndexOf('.'));
int port = 9100;
try {
port = int.parse(portController.text);
} catch (e) {
portController.text = port.toString();
}
log('subnet:\t$subnet, port:\t$port');
final stream = NetworkAnalyzer.i.discover2(subnet, port);
stream.listen((NetworkAddress addr) {
if (addr.exists) {
log('Found device: ${addr.ip}');
setState(() {
devices.add(addr.ip);
found = devices.length;
});
}
})
..onDone(() {
setState(() {
isDiscovering = false;
found = devices.length;
});
})
..onError((dynamic e) {
const snackBar = SnackBar(
content: Text('Unexpected exception', textAlign: TextAlign.center));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
});
}
Future<void> testReceipt(NetworkPrinter printer) async {
printer.text(
'Regular: aA bB cC dD eE fF gG hH iI jJ kK lL mM nN oO pP qQ rR sS tT uU vV wW xX yY zZ');
printer.text('Special 1: àÀ èÈ éÉ ûÛ üÜ çÇ ôÔ',
styles: const PosStyles(codeTable: 'CP1252'));
printer.text('Special 2: blåbærgrød',
styles: const PosStyles(codeTable: 'CP1252'));
printer.text('Bold text', styles: const PosStyles(bold: true));
printer.text('Reverse text', styles: const PosStyles(reverse: true));
printer.text('Underlined text',
styles: const PosStyles(underline: true), linesAfter: 1);
printer.text('Align left', styles: const PosStyles(align: PosAlign.left));
printer.text('Align center',
styles: const PosStyles(align: PosAlign.center));
printer.text('Align right',
styles: const PosStyles(align: PosAlign.right), linesAfter: 1);
printer.row([
PosColumn(
text: 'col3',
width: 3,
styles: const PosStyles(align: PosAlign.center, underline: true),
),
PosColumn(
text: 'col6',
width: 6,
styles: const PosStyles(align: PosAlign.center, underline: true),
),
PosColumn(
text: 'col3',
width: 3,
styles: const PosStyles(align: PosAlign.center, underline: true),
),
]);
printer.text('Text size 200%',
styles: const PosStyles(
height: PosTextSize.size2,
width: PosTextSize.size2,
));
// 打印图像
final ByteData data = await rootBundle.load('assets/logo.png');
final Uint8List bytes = data.buffer.asUint8List();
final Image? image = decodeImage(bytes);
if (image != null) {
printer.image(image);
}
// 使用替代命令打印图像
// printer.imageRaster(image);
// printer.imageRaster(image, imageFn: PosImageFn.graphics);
// 打印条形码
final List<int> barData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 4];
printer.barcode(Barcode.upcA(barData));
// 打印混合文本(中文+拉丁文)。仅适用于支持日文模式的打印机
// printer.text(
// 'hello ! 中文字 # world @ éphémère &',
// styles: PosStyles(codeTable: PosCodeTable.westEur),
// containsChinese: true,
// );
printer.feed(2);
printer.cut();
}
Future<void> printDemoReceipt(NetworkPrinter printer) async {
// 打印图像
final ByteData data = await rootBundle.load('assets/rabbit_black.jpg');
final Uint8List bytes = data.buffer.asUint8List();
final Image? image = decodeImage(bytes);
if (image != null) {
printer.image(image);
}
printer.text('GROCERYLY',
styles: const PosStyles(
align: PosAlign.center,
height: PosTextSize.size2,
width: PosTextSize.size2,
),
linesAfter: 1);
printer.text('889 Watson Lane',
styles: const PosStyles(align: PosAlign.center));
printer.text('New Braunfels, TX',
styles: const PosStyles(align: PosAlign.center));
printer.text('Tel: 830-221-1234',
styles: const PosStyles(align: PosAlign.center));
printer.text('Web: www.example.com',
styles: const PosStyles(align: PosAlign.center), linesAfter: 1);
printer.hr();
printer.row([
PosColumn(text: 'Qty', width: 1),
PosColumn(text: 'Item', width: 7),
PosColumn(
text: 'Price',
width: 2,
styles: const PosStyles(align: PosAlign.right)),
PosColumn(
text: 'Total',
width: 2,
styles: const PosStyles(align: PosAlign.right)),
]);
printer.row([
PosColumn(text: '2', width: 1),
PosColumn(text: 'ONION RINGS', width: 7),
PosColumn(
text: '0.99',
width: 2,
styles: const PosStyles(align: PosAlign.right)),
PosColumn(
text: '1.98',
width: 2,
styles: const PosStyles(align: PosAlign.right)),
]);
printer.row([
PosColumn(text: '1', width: 1),
PosColumn(text: 'PIZZA', width: 7),
PosColumn(
text: '3.45',
width: 2,
styles: const PosStyles(align: PosAlign.right)),
PosColumn(
text: '3.45',
width: 2,
styles: const PosStyles(align: PosAlign.right)),
]);
printer.row([
PosColumn(text: '1', width: 1),
PosColumn(text: 'SPRING ROLLS', width: 7),
PosColumn(
text: '2.99',
width: 2,
styles: const PosStyles(align: PosAlign.right)),
PosColumn(
text: '2.99',
width: 2,
styles: const PosStyles(align: PosAlign.right)),
]);
printer.row([
PosColumn(text: '3', width: 1),
PosColumn(text: 'CRUNCHY STICKS', width: 7),
PosColumn(
text: '0.85',
width: 2,
styles: const PosStyles(align: PosAlign.right)),
PosColumn(
text: '2.55',
width: 2,
styles: const PosStyles(align: PosAlign.right)),
]);
printer.hr();
printer.row([
PosColumn(
text: 'TOTAL',
width: 6,
styles: const PosStyles(
height: PosTextSize.size2,
width: PosTextSize.size2,
)),
PosColumn(
text: '\$10.97',
width: 6,
styles: const PosStyles(
align: PosAlign.right,
height: PosTextSize.size2,
width: PosTextSize.size2,
)),
]);
printer.hr(ch: '=', linesAfter: 1);
printer.row([
PosColumn(
text: 'Cash',
width: 8,
styles:
const PosStyles(align: PosAlign.right, width: PosTextSize.size2)),
PosColumn(
text: '\$15.00',
width: 4,
styles:
const PosStyles(align: PosAlign.right, width: PosTextSize.size2)),
]);
printer.row([
PosColumn(
text: 'Change',
width: 8,
styles:
const PosStyles(align: PosAlign.right, width: PosTextSize.size2)),
PosColumn(
text: '\$4.03',
width: 4,
styles:
const PosStyles(align: PosAlign.right, width: PosTextSize.size2)),
]);
printer.feed(2);
printer.text('Thank you!',
styles: const PosStyles(align: PosAlign.center, bold: true));
final now = DateTime.now();
final formatter = DateFormat('MM/dd/yyyy H:m');
final String timestamp = formatter.format(now);
printer.text(timestamp,
styles: const PosStyles(align: PosAlign.center), linesAfter: 2);
// 使用图像打印QR码
// try {
// const String qrData = 'example.com';
// const double qrSize = 200;
// final uiImg = await QrPainter(
// data: qrData,
// version: QrVersions.auto,
// gapless: false,
// ).toImageData(qrSize);
// final dir = await getTemporaryDirectory();
// final pathName = '${dir.path}/qr_tmp.png';
// final qrFile = File(pathName);
// final imgFile = await qrFile.writeAsBytes(uiImg.buffer.asUint8List());
// final img = decodeImage(imgFile.readAsBytesSync());
// printer.image(img);
// } catch (e) {
// log(e);
// }
// 使用原生函数打印QR码
// printer.qrcode('example.com');
printer.feed(1);
printer.cut();
}
void testlog(String printerIp, BuildContext ctx) async {
final scfMessage = ScaffoldMessenger.of(context);
const PaperSize paper = PaperSize.mm80;
final profile = await CapabilityProfile.load();
final printer = NetworkPrinter(paper, profile);
final PosPrintResult res = await printer.connect(printerIp, port: 9100);
if (res == PosPrintResult.success) {
// DEMO RECEIPT
await printDemoReceipt(printer);
// TEST PRINT
// await testReceipt(printer);
printer.disconnect();
}
final snackBar =
SnackBar(content: Text(res.msg, textAlign: TextAlign.center));
scfMessage.showSnackBar(snackBar);
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Discover Printers'),
),
body: Builder(
builder: (BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TextField(
controller: portController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: 'Port',
hintText: 'Port',
),
),
const SizedBox(height: 10),
Text('Local ip: $localIp',
style: const TextStyle(fontSize: 16)),
const SizedBox(height: 15),
ElevatedButton(
onPressed: isDiscovering ? null : () => discover(context),
child: Text(isDiscovering ? 'Discovering...' : 'Discover')),
const SizedBox(height: 15),
found >= 0
? Text('Found: $found device(s)',
style: const TextStyle(fontSize: 16))
: Container(),
Expanded(
child: ListView.builder(
itemCount: devices.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () => testlog(devices[index], context),
child: Column(
children: <Widget>[
Container(
height: 60,
padding: const EdgeInsets.only(left: 10),
alignment: Alignment.centerLeft,
child: Row(
children: <Widget>[
const Icon(Icons.print),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.center,
children: <Widget>[
Text(
'${devices[index]}:${portController.text}',
style: const TextStyle(fontSize: 16),
),
Text(
'Click to print a test receipt',
style: TextStyle(
color: Colors.grey[700]),
),
],
),
),
const Icon(Icons.chevron_right),
],
),
),
const Divider(),
],
),
);
},
),
)
],
),
);
},
),
);
}
}
更多关于Flutter热敏打印机控制插件esc_pos_printer_plus的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter热敏打印机控制插件esc_pos_printer_plus的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何使用Flutter热敏打印机控制插件esc_pos_printer_plus
的示例代码。这个插件允许你通过ESC/POS命令控制热敏打印机。以下是一个简单的示例,展示如何初始化打印机、发送打印命令以及打印文本和二维码。
首先,确保你已经在pubspec.yaml
文件中添加了esc_pos_printer_plus
依赖:
dependencies:
flutter:
sdk: flutter
esc_pos_printer_plus: ^x.y.z # 请替换为最新版本号
然后,运行flutter pub get
来获取依赖。
接下来是Flutter代码示例:
import 'package:flutter/material.dart';
import 'package:esc_pos_printer_plus/esc_pos_printer_plus.dart';
import 'package:esc_pos_printer_plus/esc_pos_bluetooth_printer.dart';
import 'package:esc_pos_printer_plus/esc_pos_usb_printer.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('ESC/POS Printer Example'),
),
body: Center(
child: PrinterExample(),
),
),
);
}
}
class PrinterExample extends StatefulWidget {
@override
_PrinterExampleState createState() => _PrinterExampleState();
}
class _PrinterExampleState extends State<PrinterExample> {
final printer = EscPosPrinterPlus();
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () async {
await printReceipt();
},
child: Text('Print Receipt'),
),
],
);
}
Future<void> printReceipt() async {
try {
// 选择打印机,这里以蓝牙打印机为例
EscPosBluetoothPrinter bluetoothPrinter = EscPosBluetoothPrinter();
List<EscPosBluetoothDevice> devices = await bluetoothPrinter.getDevices();
if (devices.isEmpty) {
throw Exception("No Bluetooth printers found");
}
EscPosBluetoothDevice device = devices.first; // 选择第一个设备,实际应用中应该让用户选择
await bluetoothPrinter.connect(device.address);
await bluetoothPrinter.open();
// 初始化打印机
await printer.init();
// 打印文本
await printer.alignCenter();
await printer.setSize(width: 2, height: 2);
await printer.text("Hello, ESC/POS Printer!\n");
await printer.text("This is a receipt example.\n\n");
// 打印二维码
String qrCodeData = "https://www.example.com";
await printer.qrCode(qrCodeData, width: 3);
await printer.cut();
// 关闭打印机连接
await bluetoothPrinter.close();
await bluetoothPrinter.disconnect();
} catch (e) {
print("Error: ${e.message}");
}
}
}
注意:
- 选择打印机:这个示例中使用了蓝牙打印机。如果你使用的是USB打印机,你需要替换为
EscPosUsbPrinter
并使用相应的方法来连接和打印。 - 权限:确保你的应用有适当的权限来访问蓝牙设备(对于Android)和访问USB设备(对于iOS和Android)。
- 错误处理:在实际应用中,你应该添加更多的错误处理逻辑,以处理各种可能的异常情况,例如连接失败、打印失败等。
这个示例展示了如何使用esc_pos_printer_plus
插件来初始化打印机、打印文本和二维码,并处理一些基本的错误情况。根据你的具体需求,你可以进一步扩展这个示例。