Flutter Android项目USB POS打印机(Epson TM-T20)打印:寻求flutter_usb_write的替代方案
替代flutter_usb_write实现USB POS打印的方案
我明白你在使用flutter_usb_write时遇到的这些糟心事——写入失败、包本身缺乏维护、不支持空安全,这些问题确实会让开发卡壳。针对Epson TM-T20这类USB POS打印机,这里有几个更靠谱的替代方案,你可以试试:
1. 使用usb_serial包
这个包是Flutter生态里比较成熟的USB串口通信工具,支持空安全,社区活跃度也不错。对于Epson打印机,你可以通过它发送原始ESC/POS指令,完全满足原始字节打印的需求。
示例代码:
import 'package:usb_serial/usb_serial.dart'; Future<void> printWithUsbSerial(String text) async { // 获取设备列表,筛选Epson TM-T20 List<UsbDevice> devices = await UsbSerial.listDevices(); UsbDevice? printerDevice = devices.firstWhere( (device) => device.productName?.contains("TM-T20") ?? false, orElse: () => throw Exception("Printer not found"), ); // 打开设备连接 UsbPort? port = await printerDevice.create(); if (!(await port!.open())) { print("Failed to open printer port"); return; } // 配置串口参数(Epson TM-T20默认参数:9600波特率、8数据位、1停止位、无校验) await port.setParameters(9600, UsbPort.DATABITS_8, UsbPort.STOPBITS_1, UsbPort.PARITY_NONE); // 准备打印数据:先发送初始化指令,再追加文本和换行 Uint8List printData = Uint8List.fromList( [27, 64] // ESC @ 指令:初始化打印机 .followedBy(text.codeUnits) .followedBy([10, 10]) // 追加两次换行,避免内容粘连 .toList(), ); // 发送打印数据 int bytesWritten = await port.write(printData); print("Successfully wrote $bytesWritten bytes to printer"); // 关闭连接 await port.close(); }
注意事项:
- 需要在AndroidManifest.xml中添加USB权限配置:
<uses-feature android:name="android.hardware.usb.host" /> <uses-permission android:name="android.permission.USB_PERMISSION" /> - 首次连接打印机时,系统会弹出USB权限授权弹窗,需要用户确认。
2. 直接调用Android原生代码(通过MethodChannel)
如果Flutter包都不能满足你的定制需求,直接写Android原生的USB通信代码是最灵活的方案,完全可控,还能避开第三方包的坑。
大致实现步骤:
- 在Android项目中创建USB打印服务类,使用Android原生的
UsbManager、UsbDeviceConnection等API实现打印机通信。 - 通过Flutter的MethodChannel将打印方法暴露给Dart层。
- 在Dart层调用原生方法完成打印。
原生代码示例(Kotlin):
class UsbPrinterHelper(private val context: Context) { private val usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager private val ACTION_USB_PERMISSION = "com.your.app.package.USB_PERMISSION" fun printText(text: String): Boolean { // 查找Epson TM-T20设备 val printerDevice = usbManager.deviceList.values.firstOrNull { it.productName?.contains("TM-T20") == true } ?: return false // 请求USB权限 val permissionIntent = PendingIntent.getBroadcast( context, 0, Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE ) usbManager.requestPermission(printerDevice, permissionIntent) // 打开设备连接 val connection = usbManager.openDevice(printerDevice) ?: return false val printerInterface = printerDevice.getInterface(0) val outputEndpoint = printerInterface.getEndpoint(0) // 组装打印数据:初始化指令 + 文本 + 换行 val initBytes = byteArrayOf(27, 64) // ESC @ 初始化 val textBytes = text.toByteArray() + byteArrayOf(10, 10) val printBytes = initBytes + textBytes // 发送数据到打印机 connection.claimInterface(printerInterface, true) val bytesWritten = connection.bulkTransfer(outputEndpoint, printBytes, printBytes.size, 5000) connection.releaseInterface(printerInterface) connection.close() // 返回是否全部字节写入成功 return bytesWritten == printBytes.size } }
之后在Flutter层通过MethodChannel调用这个printText方法即可。
3. 封装Epson官方原生SDK
Epson有针对Android的官方ePOS SDK,虽然没有直接的Flutter插件,但你可以通过MethodChannel封装原生SDK的功能。这种方式的优势是能完美适配Epson打印机,支持更多高级功能(比如切纸、条形码打印、图形打印等)。
实现思路:
- 下载并集成Android版Epson ePOS SDK到你的Flutter Android项目中。
- 封装SDK中的打印方法,通过MethodChannel暴露给Dart层。
- 在Flutter层调用封装好的方法完成打印。
另外,针对你当前使用flutter_usb_write遇到的问题,也可以简单排查下:比如是否正确申请了USB权限、是否指定了打印机的正确端点(部分打印机需要特定的bulk端点)、发送的数据是否符合ESC/POS规范。但考虑到这个包的维护状态,还是建议切换到上面更可靠的方案。
内容的提问来源于stack exchange,提问作者user643005




