Flutter应用开发:蓝牙打印

背景

做的是物流相关的业务,蓝牙打印属于核心环节。主要涉及蓝牙连接打印设备,构建打印模板,构建打印队列,打印配置。

基础依赖

开始引入的基础依赖组件是bluetooth_print,发现业务场景上不能完全满足,下载到本地自己维护二次开发。涉及Android、IOS相关原生开发,从问题上来看,主要涉及权限、配置、打印方式、ios兼容等多方面处理。

经过多次修正引入业务项目,-_-||

yaml 复制代码
dependencies:
  bluetooth_print:
    path: ../packages/logistic-btprinter-plugin

沉浸式开发

蓝牙连接

回到设备连接上,怎么搜索到、怎么连接、怎么快捷连接?为啥会有这么多问题,因为业务员不关心连接,只想打印。直接参考手机上蓝牙连接的方式。

对于业务员来说,设备基本固定很少变更,一次连接后记忆,打开打印页面直接自动连接。连接过的设备信息记录在手机存储中,根据连接频率自动尝试连接使用最频繁的蓝牙设备。本来还想进一步区分蓝牙设备的种类,如下图,然后搜出来的设备信息根本无法区分种类,遂放弃。

具体在连接上还有很多细节,很容易遇到连接卡半天、频繁出错等问题,处理好这些才能提升用户体验。

  • 状态变化的约束
  • 连接失败的重试
  • 连接成功的记忆
  • 连接异常后的处理
js 复制代码
  // status ---> not_connect | connecting | connected | disconnecting
  Future<void> _connectDevice(BluetoothDeviceModel d, {bool? setPrefs}) async {
    await bluetoothPrint.stopScan();
    // 断开已有链接
    await _disconnectDevice();
    if (d.address != null) {
      if (d.status == 'connected') return;
      // 对接蓝牙设备
      setState(() {
        d.status = 'connecting';
      });

      G.print.jsonStr(d, '连接设备');

      await bluetoothPrint.connect(d);
      bluetoothPrint.state.listen((state) {
        switch (state) {
          case BluetoothPrint.CONNECTED:
            if (mounted) {
              setState(() {
                _connected = true;
                d.status = 'connected';
                _connectedDevice = d;
                // 移入存储
                if (setPrefs == true) {
                  _setDevicePrefs(_connectedDevice);
                }
              });
            }
            break;
          default:
            break;
        }
      }, onError: (object) {
        G.print.info('蓝牙连接-${object.toString()}', '连接状态Error');
      }, onDone: () {});

      int retryTimes = 20;
      while (retryTimes > 0) {
        retryTimes--;
        bool? isConnected = await bluetoothPrint.isConnected;
        if (isConnected == null || !isConnected) {
          sleep(Duration(milliseconds: 500));
          continue;
        } else {
          _connected = true;
          // 移入存储
          if (setPrefs == true) {
            _setDevicePrefs(_connectedDevice);
          }
          setState(() {
            d.status = 'connected';
            _connectedDevice = d;
          });
          G.toast('蓝牙设备连接成功');
          break;
        }
      }

      if (!_connected && mounted) {
        setState(() {
          d.status = 'not_connect';
        });
        G.toast('蓝牙设备连接失败');
      }
      if (!mounted) return;
    } else {
      setState(() {
        tips = '请选择打印设备';
        d.status = 'not_connect';
      });
      G.toast('请选择打印设备');
    }
  }

构建打印模板

打印模板完全是定制化的,不可能随便拿个邮政、顺风物流等打印模板就能用。打印模板对于代码来说就是一段字符串,不过遵循打印机CPCL编程,参考语法开发就行。涉及文本、位置、一维码、二维码、图片、线条等元素组合,还是需要研究一下的。

打印模板最好不要放置在项目代码中,放置到服务端存储成配置项。根据不同的业务场景可能随时可能替换,就比如当前应用对接一家业务公司就得定制化一套他们觉得OK的打印模板,其实他们就是想换LOGO而已。

构建打印队列

这个点也是很关键,因为一个订单下可能有很多运单,一个运单可能要打印两张,打印过程本身是异步过程。一个异步队列的必要条件集齐了。

维护队列的顺序、上限,因为是按需打印,需要同步维护打印状态,保证不遗漏一个单,不冗余一个单ヾ(◍°∇°◍)ノ゙。

js 复制代码
    // 判断设备连接情况
    if (_connectedDevice.status != 'connected') return;
    G.loading.show(context, text: '打印中...');
    if (_waybills.length > 0) {
      // 构建打印队列,维护顺序
      TaskQueueUtils taskQueueUtils = TaskQueueUtils.instance;
      taskQueueUtils.addListener(() {
        setState(() {
          _isPrinting = taskQueueUtils.isTaskRunning;
        });
      });

      _waybills.forEach((v) {
        taskQueueUtils.addTask(_printWaybill(v));
      });

      G.loading.hide(context);
    }

打印配置

打印配置分两个场景

打印模板相关配置

  • 单联打印、双联打印就需要在打印模板上配置
  • 打印模板的宽高适配打印纸也需在打印模板上设置

自定义配置

一次的打印上限控制队列上限即可

后记

Flutter在蓝牙打印上有多个第三方插件,结合自身业务场景慎重选择。打印细节上有优化空间,多测试多优化才能把功能做好。ヾ(◍°∇°◍)ノ゙

相关推荐
Geek_Vison4 小时前
保险APP已经用了Flutter,还能接入小程序吗?
flutter·小程序·uni-app·mpaas·小程序容器
FungLeo4 小时前
成为全栈·Flutter App 篇·go_router 与四 Tab App Shell:保留导航状态、深链和登录回跳
flutter·移动开发·成为全栈·go_router·路由设计·深链接
m0_738185824 小时前
Flutter 鸿蒙化实战:screen_protector 适配 OpenHarmony,防截屏/防泄露
flutter·华为·harmonyos·鸿蒙
梦想的颜色10 小时前
【编程实战】AI 时代 APP 开发全栈硬核指南:技术选型 + AI 架构 + 模型落地全维度决策
python·flutter·react native·react.js·ai·桌面应用·milvus
yuanlaile1 天前
2026年全新Flutter 跨平台开发全栈学习资料整理
flutter·getx·鸿蒙 next·flutter开发鸿蒙·flutter入门实战·flutter全栈
恋猫de小郭2 天前
Genkit Dart 1.0 发布,Flutter 原生的 AI Agent 终于完整了
android·前端·flutter
第10086个小白2 天前
flutter桌面端打包方式(Inno Setup Compiler)版本
前端·flutter
陆断枫2 天前
Flutter 3.47 动画引擎盘点:Impeller 全平台,和那些"新特性"
flutter
程序员老刘2 天前
Flet 1.0 思路很好,可惜来晚了
flutter·ai编程·客户端
m0_738185823 天前
Flutter 鸿蒙化实战:qr_code_scanner_plus 适配 OpenHarmony,二维码扫描
flutter·华为·harmonyos·鸿蒙