Flutter中如何直接打印PDF?POS应用热敏打印需求咨询
实现Flutter直接打印现成PDF收据到热敏打印机的方案
方案一:使用printing包直接打印PDF字节流
printing是Flutter生态中适配多平台的打印工具,支持直接加载现成PDF的字节流发起打印,无需重新生成收据内容。
步骤:
- 向
pubspec.yaml添加依赖:dependencies: printing: ^5.12.0 pdf: ^3.10.0 # 包内部依赖,无需用来创建PDF - 从后端获取PDF字节流(比如通过HTTP请求拿到
Uint8List格式的数据) - 调用打印方法:
import 'package:printing/printing.dart'; import 'package:pdf/pdf.dart'; // 传入从后端获取到的pdfBytes执行打印 Future<void> printPdfReceipt(Uint8List pdfBytes) async { try { await Printing.layoutPdf( onLayout: (PdfPageFormat format) async => pdfBytes, ); } catch (e) { // 处理打印异常:打印机未连接、权限不足等 print('打印失败:$e'); } } - 热敏打印机适配:确保打印机已通过蓝牙/WiFi/USB与设备配对,调用打印后会唤起系统打印选择界面,直接选中目标热敏打印机即可。
方案二:Android平台原生调用系统打印服务
若需要更底层的控制,Android可通过MethodChannel调用原生代码,直接传递PDF字节流给系统打印服务。
步骤:
- Flutter侧通过MethodChannel调用原生方法:
import 'package:flutter/services.dart'; class PrintService { static const MethodChannel _channel = MethodChannel('print_pdf'); static Future<void> printPdfFromBytes(Uint8List pdfBytes) async { try { await _channel.invokeMethod('printPdf', {'bytes': pdfBytes}); } on PlatformException catch (e) { print('打印错误:${e.message}'); } } } - Android原生(Kotlin)实现打印逻辑:
class MainActivity : FlutterActivity() { private val CHANNEL = "print_pdf" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "printPdf") { val bytes = call.argument<ByteArray>("bytes") bytes?.let { printPdf(it) result.success(null) } ?: result.error("NULL_ERROR", "PDF字节流为空", null) } else { result.notImplemented() } } } private fun printPdf(pdfBytes: ByteArray) { val printManager = getSystemService(Context.PRINT_SERVICE) as PrintManager val jobName = "POS收据打印" val printAdapter = object : PrintDocumentAdapter() { override fun onWrite(pages: Array<out PageRange>?, destination: ParcelFileDescriptor?, cancellationSignal: CancellationSignal?, callback: WriteResultCallback?) { val fileOutputStream = FileOutputStream(destination?.fileDescriptor) fileOutputStream.write(pdfBytes) fileOutputStream.close() callback?.onWriteFinished(arrayOf(PageRange.ALL_PAGES)) } override fun onLayout(oldAttributes: PrintAttributes?, newAttributes: PrintAttributes?, cancellationSignal: CancellationSignal?, callback: LayoutResultCallback?, extras: Bundle?) { val info = PrintDocumentInfo.Builder(jobName) .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT) .build() callback?.onLayoutFinished(info, true) } } printManager.print(jobName, printAdapter, PrintAttributes.Builder().build()) } } - 权限配置:在
AndroidManifest.xml中添加必要权限:<uses-permission android:name="android.permission.INTERNET" /> <!-- 从网络获取PDF时需要 --> <uses-permission android:name="android.permission.BLUETOOTH" /> <!-- 蓝牙打印机配对时需要 -->
方案三:集成品牌专属热敏打印机SDK
如果使用佳博、芯烨这类特定品牌的热敏打印机,可直接集成其官方Flutter SDK,这类SDK通常支持直接传入PDF字节流打印,兼容性和针对性更强。
示例逻辑:
- 向
pubspec.yaml添加对应品牌SDK的依赖 - 初始化打印机连接(蓝牙/WiFi)
- 调用SDK打印方法:
// 假设已完成打印机连接初始化 Future<void> printReceiptWithSdk(Uint8List pdfBytes) async { try { // 调用SDK提供的PDF打印接口 await PrinterSdk.printPdf(pdfBytes); } catch (e) { print('SDK打印失败:$e'); } }
关键注意事项
- PDF尺寸适配:热敏打印机多为窄幅(如80mm宽),需确保后端返回的PDF尺寸与打印机匹配,避免内容被截断。
- 连接校验:打印前需确认蓝牙/WiFi/USB打印机已正常连接,可添加连接状态检测逻辑。
- 异常处理:捕获打印机未找到、打印队列溢出、权限不足等异常,给用户明确提示。
内容的提问来源于stack exchange,提问作者Adi
相关产品推荐
相关产品推荐

