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) {
      // 处理扫码异常
    }
    • onEvent 和 onError 是 PdaListenerMixin 中定义的抽象或具体方法。
    • @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('扫码异常');
  }
  
}

后记

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

相关推荐
2601_9495758613 小时前
JSON5 跨端三方库仓颉适配全流程
前端·javascript·flutter
法的空间13 小时前
AI 编程时代,为什么 Dart 分析会拖慢大型项目的开发
android·flutter·ios
恋猫de小郭15 小时前
Flutter 紧急发布 3.47.8 安全升级版本,建议都升级
android·前端·flutter
好的佩奇1 天前
Dart 之模式
android·flutter·dart
淡淡的香烟1 天前
Flutter实现多语言切换
android·flutter·1024程序员节
事圆则缓2 天前
从零搭建一套 Flutter 架构:先跑通数据流
flutter
程序员老刘2 天前
Flutter版本选择指南:3.44稳定了11周正式转正 | 2026年10月
flutter·ai编程·客户端
chicken_h3 天前
【无标题】
flutter·打包
事圆则缓3 天前
Flutter 底层渲染引擎怎么变了:从 Skia 到 Impeller,以及迁移时的坑
flutter
码林鼠3 天前
flutter状态管理入门教程-Riverpod
开发语言·javascript·flutter