Flutter开发:扫码枪应用

背景

在物流领域中,"把枪" 通常指手持终端设备(Handheld Terminal) ,也被称为 "物流扫码枪""数据采集器" 等。最近做的物流项目,把枪上的应用也是必要环节。

"把枪" 对物流行业的价值

  • 提升效率:替代人工录入,单票货物信息处理时间从分钟级缩短至秒级,分拣效率提升 50% 以上。
  • 降低误差:条码扫描准确率接近 100%,减少人工记录导致的错单、漏单问题。
  • 数据可视化:实时数据同步帮助企业监控物流全流程,优化库存管理、运输路线等。
  • 成本控制:减少人力投入(如仓储盘点人员可减少 30%-50%),长期降低运营成本。

Flutter业务项目

把枪终端 现在基本外观已经类手机化,能承载更为复杂的功能。Flutter上开发基本和普通APP开发一致,只是业务重点全部在 "扫码" 业务。如下图,支持了扫码上车、卸车、揽货、共享。

终端按钮触发扫码动作,扫码运单号后直接发接口对接业务,为了避免扫码遗漏,需要增加清晰的反馈音和记录。

记录

扫码记录直接存在终端存储里,使用SharedPreferences管理存储。当然也可以持久化在数据中进行管理,目前业务上把枪操作员的把枪隶属个人,数据不做采集监控。

js 复制代码
    // 初始化
  void _initErrorScanResult() async {
    SharedPreferences _prefs = await SharedPreferences.getInstance();
    String loadErrorStr = _prefs.get('loadResult') != null ? _prefs.get('loadResult').toString() : '[]';
    var loadErrors = json.decode(loadErrorStr);
    List<ScanResData> _allRecords = (loadErrors as List<dynamic>).map((e) => ScanResData.fromJson(e)).toList();
    setState(() {
      _successRecords = _allRecords.where((v) => v.status == 'success').toList();
      _errRecords = _allRecords.where((v) => v.status == 'error').toList();
    });
  }
  
  
    // 移除
  void _remove(String target, String status, int index) async {
    setState(() {
      if (status == 'error') {
        _errRecords = _errRecords.asMap().entries.where((indexValue) => indexValue.key != index).map((v) => v.value).toList();
      } else {
        _successRecords = _successRecords.asMap().entries.where((indexValue) => indexValue.key != index).map((v) => v.value).toList();
      }

      _reset();
    });
  }
   // 重置
  Future<void> _reset() async {
    SharedPreferences _prefs = await SharedPreferences.getInstance();
    List<ScanResData> _mergeRecords = [];
    _mergeRecords.addAll(_errRecords);
    _mergeRecords.addAll(_successRecords);
    _prefs.setString('unloadResult', json.encode(_mergeRecords));
  }

扫码防干扰

如下图物流包裹上贴的快递单,在扫码时上面的一维码以及下面的二维码都会触发把枪扫描成功,因为扫码距离的原因,有一定概率扫上二维码,影响操作效率。在事件回调中排除掉二维码链接。

js 复制代码
  @override
  void onEvent(Object event) {
    G.print.info(event.toString(), '扫码结果');
    String? code = event as String;
    if (Utils.isLink(code)) return;
    if (code.isNotEmpty && !_saving) {
      setState(() {
        formData.waybillNo = code;
        formData.waybillNoList = [code];
        _save();
      });
    } else {}
  }

扫码插件

扫码插件使用的是pda_scanner 0.2.9,This is a scanning plug-in for PDA to listen the scanned events and get scanning results.

pda_scanner 插件是用于 Flutter 应用与 PDA(掌上电脑)扫码设备对接的中间件。它通过特定的通信机制实现应用与硬件设备的交互。因为flutter升级缘故,拉到本地进行管理。

js 复制代码
  pda_scanner:
    path: ../packages/logistic-pda-plugin

使用也很简单

1. with PdaListenerMixin<PdaScanLoad> 的作用

Mixin 混入机制

  • 概念:Mixin 是 Dart 中的一种代码复用方式,允许类在不继承的情况下使用其他类的方法和属性。

  • 语法 :通过 with 关键字将 Mixin 添加到类中。

  • 示例代码分析

    dart

    scala 复制代码
    class _PdaScanLoadState extends State<PdaScanLoad> with PdaListenerMixin<PdaScanLoad> {
      // ...
    }
    • _PdaScanLoadState 类继承自 State<PdaScanLoad>,同时混入了 PdaListenerMixin
    • PdaListenerMixin<PdaScanLoad> 是一个泛型 Mixin,它提供了监听 PDA 扫描设备的功能(如扫码回调、错误处理)。
    • 优势 :无需继承复杂的类层次结构,直接复用 PdaListenerMixin 的功能。

2. @override 注解的作用

方法重写(Override)

  • 概念:子类重新实现父类或 Mixin 中已有的方法。

  • 语法 :使用 @override 注解标记重写的方法(非强制,但推荐)。

  • 示例代码分析

    dart

    less 复制代码
    @override
    void onEvent(Object event) {
      // 处理扫码事件
    }
    
    @override
    void onError(Object error) {
      // 处理扫码异常
    }
    • onEventonErrorPdaListenerMixin 中定义的抽象或具体方法。
    • @override 明确告诉编译器:这个方法是重写父类或 Mixin 的方法,避免拼写错误。

3. 结合场景:PDA 扫码功能

  • Mixin 的功能PdaListenerMixin 提供了监听 PDA 设备的能力,可能包含:

    • 初始化 PDA 设备连接
    • 接收扫码事件的回调机制
    • 错误处理逻辑
  • 重写的目的

    • onEvent:处理扫码结果(如保存运单号)。
    • onError:处理扫码异常(如播放提示音)。
js 复制代码
import 'package:pda_scanner/pda_listener_mixin.dart';

class _PdaScanLoadState extends State<PdaScanLoad> with PdaListenerMixin<PdaScanLoad> {

  @override
  void onEvent(Object event) {
    G.print.info(event.toString(), '扫码结果');
    String? code = event as String;
    if (Utils.isLink(code)) return;
    if (code.isNotEmpty && !_saving) {
      setState(() {
        formData.waybillNo = code;
        formData.waybillNoList = [code];
        _save();
      });
    } else {}
  }

  @override
  void onError(Object error) {
    G.print.info(error.toString(), '扫码异常日志');
    Utils.speakToast('扫码异常');
  }
  
}

后记

刚开始接触时,因为是新型终端的缘故,心理上还是比较有压力。实际接触扫码枪实物并使用后,发现在场景相对单一的终端上难度很小。因为扫码机型号的统一装配使用,也没有各类型手机应用带来的诸多兼容性问题。扫码机相对比较有质感,看起来很皮实,虽然基本功能类手机,不过应用做的越简单越好。

相关推荐
无知的前端5 小时前
Flutter开发,GetX框架路由相关详细示例
android·flutter·ios
icc_tips18 小时前
Flutter 的Async/Await 日常使用
flutter
造梦师18 小时前
flutter基础面试知识汇总(二)
flutter
vvilkim18 小时前
Flutter 核心概念:深入理解 StatelessWidget 与 StatefulWidget
开发语言·javascript·flutter
sunly_18 小时前
Flutter:导航背景固定在顶部,下拉分页布局
开发语言·javascript·flutter
明似水18 小时前
使用 Melos 高效管理 Flutter/Dart Monorepo 项目
flutter
zacksleo21 小时前
哪些鸿蒙原生应用在使用Flutter
前端·flutter·harmonyos
vvilkim1 天前
Flutter布局系统全面解析:从基础组件到复杂界面构建
flutter
程序员老刘1 天前
MCP:新时代的API,每个程序员都应该掌握
人工智能·flutter·mcp
恋猫de小郭1 天前
Flutter 小技巧之:实现 iOS 26 的 “液态玻璃”
android·前端·flutter