Flutter蓝牙热敏打印机打印QR码/图片失败问题排查
Flutter蓝牙热敏打印机无法打印QR码/图片的解决方案
问题背景
使用flutter_blue和flutter_esc_pos_utils实现蓝牙热敏打印,文本打印正常,但以下两种方式均无法打印QR码:
- 调用
gen.qrcode()直接生成QR码 - 加载本地图片转成
img.Image后调用gen.image()打印
调试发现打印图片时数据全为0,尝试Base64转图片仅打印字符串,还出现过大量问号的异常。
相关代码片段
打印核心函数
void printWithDevice(BluetoothDevice device) async { final profile = await CapabilityProfile.load(); final gen = Generator(PaperSize.mm58, profile); final printer = BluePrint(); final ByteData data = await rootBundle.load('assets/images/qr.png'); final Uint8List bytes = data.buffer.asUint8List(); final img.Image image = img.decodeImage(bytes)!; printer.add(gen.image(image)); // 无效 printer.add(gen.qrcode('www.google.com')); // 无效 printer.add(gen.text('hello')); // 有效 await printer.printData(device); }
BluePrint类实现
class BluePrint { BluePrint({this.chunkLen = 512}); final int chunkLen; final _data = List<int>.empty(growable: true); void add(List<int> data) { _data.addAll(data); } List<List<int>> getChunks() { final chunks = List<List<int>>.empty(growable: true); for (var i = 0; i < _data.length; i += chunkLen) { chunks.add(_data.sublist(i, min(i + chunkLen, _data.length))); } return chunks; } Future<void> printData(BluetoothDevice device) async { final data = getChunks(); final characs = await _getCharacteristics(device); for (var i = 0; i < characs.length; i++) { if (await _tryPrint(characs[i], data)) { break; } } } Future<bool> _tryPrint( BluetoothCharacteristic charc, List<List<int>> data, ) async { for (var i = 0; i < data.length; i++) { try { await charc.write(data[i]); } catch (e) { return false; } } return true; } Future<List<BluetoothCharacteristic>> _getCharacteristics( BluetoothDevice device, ) async { final services = await device.discoverServices(); final res = List<BluetoothCharacteristic>.empty(growable: true); for (var i = 0; i < services.length; i++) { res.addAll(services[i].characteristics); } return res; } }
尝试的QR码生成方式(无效)
String qrData = "google.com"; const double qrSize = 200; try { 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()); generator.image(img); } catch (e) { print(e); }
修复方案
1. 过滤有效蓝牙特征值
当前代码遍历所有特征值,但只有支持写入的特征值才能传输打印命令。修改_getCharacteristics方法:
Future<List<BluetoothCharacteristic>> _getCharacteristics(BluetoothDevice device) async { final services = await device.discoverServices(); final res = List<BluetoothCharacteristic>.empty(growable: true); for (var service in services) { // 只保留可写入的特征值 res.addAll(service.characteristics.where((c) => c.properties.write || c.properties.writeWithoutResponse )); } return res; }
2. 修复图片解码与QR码生成逻辑
- 避免强制解包可能为null的图片对象,添加空判断:
final ByteData data = await rootBundle.load('assets/images/qr.png'); final Uint8List bytes = data.buffer.asUint8List(); final img.Image? image = img.decodeImage(bytes); if(image != null){ printer.add(gen.image(image)); } else { print("图片解码失败,请检查图片格式"); }
- 直接使用
gen.qrcode()生成,添加刷新缓冲区命令:
// 控制QR码大小(3-5为常规范围),添加换行/切纸确保输出 printer.add(gen.qrcode('www.google.com', size: 4)); printer.add(gen.newline()); printer.add(gen.cut());
3. 调整蓝牙写入的Chunk大小
默认512的Chunk可能超过打印机MTU,改为更小的数值:
BluePrint({this.chunkLen = 64}); // 先尝试64,若仍有问题可改为20
或动态获取设备MTU:
Future<void> printData(BluetoothDevice device) async { final mtu = await device.mtu; final chunkLen = mtu - 3; // 预留蓝牙协议头空间 final data = _getChunks(chunkLen); // 需修改getChunks方法接收参数 // ... 后续逻辑 }
4. 添加打印机初始化命令
打印前发送重置命令,确保打印机处于就绪状态:
final gen = Generator(PaperSize.mm58, profile); printer.add(gen.reset()); // 初始化打印机
5. 调试建议
- 打印
gen.image()或gen.qrcode()返回的List长度,若为0则说明数据生成失败,重点排查图片解码逻辑。 - 在
_tryPrint中打印每个Chunk的内容,确认是否为有效数据而非全0。
内容的提问来源于stack exchange,提问作者pranshul
相关产品推荐
相关产品推荐

