跳过主要内容

How to Build an Ionic Barcode Scanner with Capacitor

使用Capacitor构建一个Ionic条形码扫描器8:设置应用,添加@capacitor/条形码扫描器,扫描iOS,Android和Web上的QR码和条形码。

文章来源

马丁·多纳迪尤

作者

Valeria

审阅者

乔丹

编辑

How to Build an Ionic Barcode Scanner with Capacitor

为了在Ionic中创建一个条码扫描器,使用Capacitor,首先创建一个集成了Capacitor的Ionic应用,安装官方的Capacitor条码扫描器插件。 @capacitor/barcode-scanner 该插件在iOS和Android上打开一个原生全屏扫描器,在Web上打开一个浏览器扫描器,并返回解码的文本和格式。 minSdkVersion 本教程在Ionic Angular 8上构建一个工作的扫描器应用,涵盖格式过滤、错误处理、Android扫描引擎、连续扫描、企业硬件扫描器和常见故障。 CapacitorBarcodeScanner.scanBarcode() 您将构建

This tutorial builds a working scanner app in Ionic Angular on Capacitor 8, then covers format filtering, error handling, Android scanning engines, continuous scanning, enterprise hardware scanners, and common failures.

该应用将使用__CAPGO_KEEP_0__扫描条形码,__CAPGO_KEEP_1__将扫描条形码并返回解码的文本和格式。

该应用将使用__CAPGO_KEEP_0__扫描条形码,__CAPGO_KEEP_1__将扫描条形码并返回解码的文本和格式。

  • 打开相机扫描器
  • 显示每个结果的格式
  • 让您复制或打开扫描值
  • 清洁地处理取消和权限错误

该code使用Angular独立组件。插件调用在React或Vue中相同,只是模板语法发生了变化。

先决条件

  • Node.js LTS和Bun。
  • 对于Android:Android Studio与SDK,以及具有摄像头的设备或模拟器。
  • 对于iOS:一台Mac,Xcode 26,App Store Connect要求从2026年4月起上传。
  • 物理设备最好。模拟器可以使用虚拟摄像头扫描,但真实的照明和焦距对于条形码很重要。

步骤1:创建Ionic应用

bunx @ionic/cli start barcode-scanner blank --type=angular-standalone --capacitor
cd barcode-scanner

The Ionic CLI scaffolds the Angular app with @capacitor/core 和 @capacitor/cli 已经安装。检查 capacitor.config.ts: appId 应该是你的反向域名ID,例如 com.example.scanner,和 webDir 应该与构建输出匹配(www 对于Ionic Angular)。

只需构建一次,使原生平台有东西可以复制:

bun run build

步骤2:添加Android和iOS

bun add @capacitor/android @capacitor/ios
bunx cap add android
bunx cap add ios

Capacitor 8会创建新的iOS项目使用Swift Package Manager。如果您正在升级较旧的应用程序,请遵循 Capacitor 8升级指南 首先。

步骤3:安装条形码扫描器插件

bun add @capacitor/barcode-scanner
bunx cap sync

@capacitor/barcode-scanner 是Capacitor团队的官方扫描器。版本3.x支持Capacitor8。

Android配置

该插件需要AndroidAPI26或更高版本。Capacitor8默认为24,因此编辑 android/variables.gradle:

ext {
    minSdkVersion = 26
    // keep the other values Capacitor generated
}

在Android上,您可以根据扫描选择解码引擎:ZXing(默认,支持插件列出的所有格式)或Google ML Kit(在困难代码上快速,但不支持MaxiCode、RSS-14、RSS扩展或UPC/EAN扩展)。

iOS配置

添加一个摄像头使用说明到 ios/App/App/Info.plist:

<key>NSCameraUsageDescription</key>
<string>The camera is used to scan barcodes and QR codes.</string>

没有它,iOS会在首次摄像头访问时终止应用。为用户写一句句子,因为App Review也会读它。iOS使用Apple的Vision框架,该框架支持除MaxiCode和UPC/EAN扩展外的所有列出的格式,并且不区分UPC-A和EAN-13。

步骤4:写扫描服务

在页面/区域:Capgo页面。角色:UI标签。见于:关于.astro页面。消息键`about_how_step_label`(关于如何步骤标签)。

// src/app/services/scanner.service.ts
import { Injectable } from '@angular/core';
import { Capacitor } from '@capacitor/core';
import {
  CapacitorBarcodeScanner,
  CapacitorBarcodeScannerAndroidScanningLibrary,
  CapacitorBarcodeScannerCameraDirection,
  CapacitorBarcodeScannerScanOrientation,
  CapacitorBarcodeScannerTypeHint,
  CapacitorBarcodeScannerTypeHintALLOption,
} from '@capacitor/barcode-scanner';

export interface ScanResult {
  value: string;
  format: string;
  scannedAt: Date;
}

export type ScanOutcome =
  | { status: 'ok'; result: ScanResult }
  | { status: 'cancelled' }
  | { status: 'denied' }
  | { status: 'error'; message: string };

@Injectable({ providedIn: 'root' })
export class ScannerService {
  async scan(onlyQr = false): Promise<ScanOutcome> {
    try {
      const { ScanResult, format } = await CapacitorBarcodeScanner.scanBarcode({
        hint: onlyQr
          ? CapacitorBarcodeScannerTypeHint.QR_CODE
          : CapacitorBarcodeScannerTypeHintALLOption.ALL,
        scanInstructions: 'Point the camera at a barcode',
        cameraDirection: CapacitorBarcodeScannerCameraDirection.BACK,
        scanOrientation: CapacitorBarcodeScannerScanOrientation.ADAPTIVE,
        cancelButtonAccessibilityLabel: 'Cancel scanning',
        torchButtonOnAccessibilityLabel: 'Turn flashlight off',
        torchButtonOffAccessibilityLabel: 'Turn flashlight on',
        android: {
          scanningLibrary: CapacitorBarcodeScannerAndroidScanningLibrary.MLKIT,
        },
        web: {
          showCameraSelection: true,
        },
      });

      return {
        status: 'ok',
        result: {
          value: ScanResult,
          format: CapacitorBarcodeScannerTypeHint[format] ?? String(format),
          scannedAt: new Date(),
        },
      };
    } catch (err: any) {
      const code: string = err?.code ?? '';
      if (code.endsWith('0006')) return { status: 'cancelled' };
      if (code.endsWith('0007')) return { status: 'denied' };
      return { status: 'error', message: err?.message ?? 'Scan failed' };
    }
  }

  get platform() {
    return Capacitor.getPlatform();
  }
}

将插件调用放在服务中使它们易于重用和测试。 OS-PLUG-BARC-XXXX该插件以代码形式返回错误。 0006 .在iOS上, 0007 means camera access was denied. Treat cancel as a normal outcome, not an error. Log the raw code on Android during testing to confirm the same mapping on your target devices.

在结果中 format 转换回一个数字枚举值 CapacitorBarcodeScannerTypeHint[format] 例如 QR_CODE 或 EAN_13.

在Capacitor live-update替代方案比较页面中,

// src/app/home/home.page.ts
import { Component, inject, signal } from '@angular/core';
import { DatePipe } from '@angular/common';
import {
  IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem, IonLabel,
  IonFab, IonFabButton, IonIcon, IonToggle, IonNote, ToastController,
} from '@ionic/angular/standalone';
import { addIcons } from 'ionicons';
import { scanOutline } from 'ionicons/icons';
import { ScannerService, ScanResult } from '../services/scanner.service';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  imports: [
    DatePipe, IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem,
    IonLabel, IonFab, IonFabButton, IonIcon, IonToggle, IonNote,
  ],
})
export class HomePage {
  private scanner = inject(ScannerService);
  private toast = inject(ToastController);

  results = signal<ScanResult[]>([]);
  onlyQr = signal(false);
  scanning = signal(false);

  constructor() {
    addIcons({ scanOutline });
  }

  async scan() {
    if (this.scanning()) return;
    this.scanning.set(true);
    const outcome = await this.scanner.scan(this.onlyQr());
    this.scanning.set(false);

    if (outcome.status === 'ok') {
      this.results.update((list) => [outcome.result, ...list]);
    } else if (outcome.status === 'denied') {
      await this.show('Camera access is off. Enable it in Settings to scan.');
    } else if (outcome.status === 'error') {
      await this.show(outcome.message);
    }
  }

  private async show(message: string) {
    const t = await this.toast.create({ message, duration: 3000 });
    await t.present();
  }
}
<!-- src/app/home/home.page.html -->
<ion-header>
  <ion-toolbar>
    <ion-title>Barcode Scanner</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-list>
    <ion-item>
      <ion-toggle [checked]="onlyQr()" (ionChange)="onlyQr.set($event.detail.checked)">
        QR codes only
      </ion-toggle>
    </ion-item>

    @for (r of results(); track r.scannedAt) {
      <ion-item>
        <ion-label>
          <h2>{{ r.value }}</h2>
          <p>{{ r.format }} · {{ r.scannedAt | date: 'shortTime' }}</p>
        </ion-label>
      </ion-item>
    } @empty {
      <ion-item lines="none">
        <ion-note>No scans yet. Tap the button to scan.</ion-note>
      </ion-item>
    }
  </ion-list>

  <ion-fab slot="fixed" vertical="bottom" horizontal="end">
    <ion-fab-button (click)="scan()" [disabled]="scanning()" aria-label="Scan a barcode">
      <ion-icon name="scan-outline" aria-hidden="true"></ion-icon>
    </ion-fab-button>
  </ion-fab>
</ion-content>

在Appflow比较/迁移营销文案中,

bun run build
bunx cap sync
bunx cap run android
bunx cap run ios

在Capawesome比较页面中, bun run start 在咨询服务页面中,

步骤5:构建页面 bunx cap run android --livereload --external 步骤6:在设备上运行

对于Web版本,

格式 安卓ZXing 安卓ML Kit iOS
二维码 Code, Aztec, Data Matrix, PDF417 是 是 是
条形码 是 是 是(UPC-A和EAN-13一样)
Code 128, Code 39, Code 93, Codabar, ITF Yes Yes Yes
简易扫描 Yes No Yes
No Yes No No
UPC/EAN 扩展 Yes 是 是

No hint如果您传入一个不支持的格式作为

,插件会回退到扫描任何格式。

安全处理扫描值

  • 扫描的条码是未经信任的输入。 在对其采取行动之前:
  • 不要将扫描的字符串作为 code 运行,也不要将它们注入 HTML 中。
  • 不要将扫描的字符串作为__CAPGO_KEEP_0__运行或将其注入HTML。
  • 对于产品代码,验证校验码之前调用您的后端。

超越基本扫描器

scanBarcode 是一个一次性全屏流程。它覆盖了大多数应用程序。其他需求需要其他工具:

  • 在您的UI中进行连续扫描。 在您的Web UI后面运行一个摄像头 feed 与 @capgo/摄像头预览,捕获帧并在JavaScript中使用一个库进行解码 captureSample坚固的企业设备。 zxing-wasm.
  • 泽巴手持设备具有硬件扫描器。 @__CAPGO_KEEP_0__/__CAPGO_KEEP_1__-zebra-datawedge @capgo/capacitor-zebra-datawedge 文档捕获
  • __CAPGO_KEEP_0__=Capacitor, __CAPGO_KEEP_1__=DataWedge 对于收据和纸质表格,一个文档扫描插件比条形码扫描器更合适。见到 插件目录.

从Cordova过来的?我们的 cordova条形码扫描器指南 涵盖了旧的插件和迁移路径。

故障排除

iOS 应用程序在扫描开始时关闭。 NSCameraUsageDescription iOS应用程序在扫描开始时关闭。 Info.plist.

缺失于 minSdkVersion Android构建失败,出现manifest合并或minSdk错误。 android/variables.gradle 仍然是24。将其设置为26在

Scanner opens but never reads the code. 尝试使用其他 Android 引擎,改善照明,使用手电筒,并限制 hint 到您期望的格式。非常小或损坏的代码需要摄像头更近。

每次都拒绝权限。 用户之前拒绝了摄像头访问。显示一个解释如何在系统设置中启用它的消息。操作系统不会再次显示提示。

Web 扫描器没有启动。 页面未通过 HTTPS 或 localhost 服务,或者另一个标签持有摄像头。

平板电脑上的方向设置被忽略。 从 Android SDK 36 开始,Android 16 在大屏幕上忽略固定方向。使用 ADAPTIVE.

不必等待审查就可以将修复推送到应用程序

一旦扫描器活跃,几乎所有更改,例如新验证规则、UI 调整或不同的后端查找,都是 web code。 Capgo 实时更新 将这些推送到已安装的应用程序几分钟内,而插件升级和 Info.plist 当您需要一个新的本地构建而不需要本地Mac设置时,变化仍然会通过商店。 Capgo 构建 在云端生成签名的iOS和Android二进制文件

Wrap-up

一款Ionic条形码扫描器在Capacitor 8中只需要一个官方插件,两个本地配置修改(Android 26和iOS摄像头使用字符串),以及一个单独的 minSdkVersion 26和iOS摄像头使用字符串),和一个 scanBarcode Article credits

实时更新 Capacitor 应用

当 web 层 bug 活跃时,通过 Capgo 发布修复,而不是等待几天的 app 商店审批。用户在后台接收更新,而原生更改仍在正常审查路径中。

最新博客

Capgo 为您提供了创建真正专业的移动应用所需的最佳见解。