pda_scanner

August 21, 2026 · View on GitHub

Language: English | 中文简体

pda_scanner

License Pub

Flutter plugin that listens to hardware scan-gun broadcasts on industrial PDAs.

  • Dart 3 / Flutter 3.10+ / Android embedding v2
  • Android 13+ RECEIVER_EXPORTED (required for manufacturer scanner intents)
  • Extra-key fallback so a new OEM does not need a plugin release
  • Receivers are unregistered on detach (no leaked listeners)

Android only. Scan guns that type as a keyboard still work through Flutter text fields; this plugin is for broadcast intent mode.

Install

dependencies:
  pda_scanner: ^0.3.0

minSdk 21.

Supported manufacturers

BrandTypical actionTypical extra
SEUIC (小码哥)com.android.scanner.broadcast / com.seuic.scanner.action.SCANNER_RESULTscannerdata
iData (盈达聚力)android.intent.action.SCANRESULTvalue
Urovo (优博讯)com.android.server.scannerservice.broadcastscannerdata
Honeywellcom.honeywell.decode.intent.action.EDIT_DATAdata
Newland (新大陆)nlscan.action.SCANNER_RESULTSCAN_BARCODE1
Panlian (攀凌)scan.rcv.messagebarocode + length
Zebra DataWedgecom.symbol.datawedge.api.RESULT_ACTIONcom.symbol.datawedge.data_string
Sunmicom.sunmi.scanner.ACTION_DATA_CODE_RECEIVEDdata
CipherLabcom.cipherlab.barcodebaseapi.PASS_DATA_2_APPDecoder_Data
Datalogiccom.datalogic.decodewedge.decode_actioncom.datalogic.decode.intentwedge.barcode_string
Bluebirdkr.co.bluebird.android.bbkey.BARCODEEXTRA_BARCODE_DECODING_DATA
Speedatacom.spd.action.SCAN_CALLBACKscannerdata

Unknown OEM? The plugin still tries a list of common extras, then the first non-empty string extra. Open an issue with adb logcat of the intent if yours is missing.

Usage

Init once on the root widget, listen on each page that should receive scans.

import 'package:flutter/material.dart';
import 'package:pda_scanner/pda_scanner.dart';

class RootApp extends StatefulWidget {
  const RootApp({super.key});
  @override
  State<RootApp> createState() => _RootAppState();
}

class _RootAppState extends State<RootApp> with PdaLifecycleMixin<RootApp> {
  @override
  Widget build(BuildContext context) => const MaterialApp(home: ScanPage());
}

class ScanPage extends StatefulWidget {
  const ScanPage({super.key});
  @override
  State<ScanPage> createState() => _ScanPageState();
}

class _ScanPageState extends State<ScanPage> with PdaListenerMixin<ScanPage> {
  String? code;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(child: Text(code ?? 'Scan a barcode')),
    );
  }

  @override
  void onEvent(Object event) => setState(() => code = event.toString());

  @override
  void onError(Object error) {}
}

If the State already mixes in another type and you cannot use the mixins as-is, call the methods yourself:

  • Root: initPdaLifecycle() / disposePdaLifecycle()
  • Page: registerPdaListener() / unRegisterPdaListener()

PdaListenerMixin only fires when the route is current, so a covered page does not steal the scan.

PdaSource.latest;       // last payload
PdaSource.isListening;  // event channel is open
PdaSource.manufacturers;

Migrating from 0.2.x

  • SDK >=3.0.0 <4.0.0
  • Import package:pda_scanner/pda_scanner.dart (barrel) or the same three files as before
  • Android embedding v2 onlyregisterWith is gone
  • RaisedButton in the example is now ElevatedButton

License

MIT