Pour créer un scanner de codes-barres Ionic avec Capacitor, créez une app Ionic avec l'intégration Capacitor, puis installez le @capacitor/barcode-scanner plugin officiel, augmentez le minSdkVersion à 26, ajoutez la description d’utilisation de la caméra iOS et appelez CapacitorBarcodeScanner.scanBarcode() depuis un bouton. Le plugin ouvre un scanner natif plein écran sur iOS et Android, un scanner navigateur sur le web, et renvoie le texte décodé ainsi que le format.
Ce tutoriel crée une application de scanner fonctionnelle en Ionic Angular sur Capacitor 8, puis aborde le filtrage des formats, la gestion des erreurs, les moteurs de scan Android, le scan continu, les scanners matériels d’entreprise et les pannes courantes.
Ce que vous allez construire
Une application Ionic monopage avec un bouton de scan qui :
- ouvre le scanner de la caméra,
- affiche chaque résultat avec son format,
- vous permet de copier ou d’ouvrir la valeur scannée,
- gère proprement les erreurs d’annulation et d’autorisation.
Le code utilise des composants standalone Angular. Les appels du plugin sont identiques en React ou en Vue, seule la syntaxe du template change.
Prerequisites
- Node.js LTS et Bun.
- Pour Android : Android Studio avec un SDK et un appareil ou un émulateur avec caméra.
- Pour iOS : un Mac avec Xcode 26, requis par App Store Connect pour les uploads depuis avril 2026.
- Un appareil physique est idéal. Les émulateurs peuvent scanner via une caméra virtuelle, mais l’éclairage et la mise au point réels comptent pour les codes-barres.
Étape 1 : Créer l'application Ionic
bunx @ionic/cli start barcode-scanner blank --type=angular-standalone --capacitor
cd barcode-scanner
Le CLI Ionic génère l’application Angular avec @capacitor/core et @capacitor/cli déjà installé. Vérifiez capacitor.config.ts: appId should be your reverse-domain ID, for example com.example.scanner, et webDir doit correspondre à la sortie du build (www pour Ionic Angular).
Compilez une seule fois pour que les plateformes natives aient quelque chose à copier :
bun run build
Étape 2 : Ajouter Android et iOS
bun add @capacitor/android @capacitor/ios
bunx cap add android
bunx cap add ios
Capacitor 8 crée de nouveaux projets iOS avec Swift Package Manager. Si vous mettez à niveau une application plus ancienne, suivez le Capacitor guide de mise à niveau d'abord.
Étape 3 : Installer le plugin de scan de codes-barres
bun add @capacitor/barcode-scanner
bunx cap sync
@capacitor/barcode-scanner est le scanner officiel de l’équipe Capacitor. La version 3.x prend en charge Capacitor 8.
Configuration Android
Le plugin nécessite Android API 26 ou supérieur. Capacitor 8 est défini sur 24 par défaut, modifiez donc android/variables.gradle:
ext {
minSdkVersion = 26
// keep the other values Capacitor generated
}
Sur Android, vous pouvez choisir le moteur de décodage pour chaque scan : ZXing (par défaut, prend en charge tous les formats listés par le plugin) ou Google ML Kit (rapide sur les codes difficiles, mais sans MaxiCode, RSS-14, RSS Expanded ou extension UPC/EAN).
Configuration iOS
Add a camera usage description to ios/App/App/Info.plist:
<key>NSCameraUsageDescription</key>
<string>The camera is used to scan barcodes and QR codes.</string>
Sans cela, iOS termine l'application au premier accès à la caméra. Rédigez la phrase pour l'utilisateur, car App Review la lit aussi. iOS utilise le framework Vision d'Apple, qui prend en charge tous les formats listés sauf MaxiCode et extension UPC/EAN, et ne distingue pas UPC-A d'EAN-13.
Étape 4 : Écrire le service de scanner
Garder les appels du plugin dans un service facilite leur réutilisation et leurs tests.
// 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();
}
}
Le plugin renvoie des erreurs avec des codes de la forme OS-PLUG-BARC-XXXX. Sur iOS, 0006 signifie que l'utilisateur a annulé et 0007 signifie que l'accès à la caméra a été refusé. Traitez l'annulation comme un résultat normal, pas comme une erreur. Enregistrez le code brut sur Android pendant les tests pour confirmer le même mapping sur vos appareils cibles.
Le format dans le résultat se trouve une valeur d'enum numérique. CapacitorBarcodeScannerTypeHint[format] la reconvertit en un nom tel que QR_CODE ou EAN_13.
Étape 5 : Construire la page
// 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>
Étape 6 : Lancer sur les appareils
bun run build
bunx cap sync
bunx cap run android
bunx cap run ios
Pour la version web, bun run start sert l'app sur localhost, où les navigateurs autorisent l'accès à la caméra. Sur une IP LAN, HTTPS est nécessaire.
Pour itérer plus vite, utilisez le live reload sur un appareil : bunx cap run android --livereload --external recharge l'app à chaque sauvegarde.
Formats pris en charge
| Format | Android ZXing | Android ML Kit | iOS |
|---|---|---|---|
| QR Code, Aztec, Data Matrix, PDF417 | Oui | Oui | Oui |
| EAN-13, EAN-8, UPC-A, UPC-E | Oui | Oui | Oui (UPC-A et EAN-13 traités à l'identique) |
| Code 128, Code 39, Code 93, Codabar, ITF | Oui | Oui | Oui |
| RSS-14, RSS Expanded | Oui | Non | Oui |
| MaxiCode | Oui | Non | Non |
| Extension UPC/EAN | Oui | Non | Non |
Si vous passez un format non pris en charge en hint, le plugin bascule sur la détection de n'importe quel format.
Gérer les valeurs scannées en toute sécurité
Un code-barres est une entrée non fiable. Avant d'agir sur celle-ci :
- Validez les URL avant de les ouvrir et affichez le domaine à l'utilisateur en premier.
- Ne pas exécuter les chaînes scannées en tant que code ni les injecter dans le HTML.
- Pour les codes produit, validez le chiffre de contrôl’avant d'appeler votre backend.
- Limitez les requêtes backend si les utilisateurs scannent rapidement.
Au-delà du scanner de base
scanBarcode est un flux plein écran en une seule étape. Cela couvre la plupart des applications. D’autres besoins exigent d’autres outils :
- Scan continu dans votre propre interface. Affichez un flux caméra derrière votre interface web avec @capgo/camera-preview, capturez des images avec
captureSample, et décodez-les en JavaScript avec une bibliothèque telle quezxing-wasm. - Appareils d’entreprise robustes. Zebra handhelds have hardware scanners. @capgo/capacitor-zebra-datawedge gère les profils DataWedge et les déclencheurs de scan, ce qui est plus rapide et plus fiable que la caméra sur ces appareils.
- Capture de documents. Pour les reçus et les formulaires papier, un plugin de scan de documents est plus adapté qu’un lecteur de codes-barres. Voir la répertoire des plugins.
Vous venez de Cordova ? Notre guide du scanner de codes-barres Cordova couvre les anciens plugins et un chemin de migration.
Troubleshooting
L’application iOS se ferme au démarrage du scan. NSCameraUsageDescription manque dans Info.plist.
Le build Android échoue avec une erreur de manifest merger ou de minSdk. minSdkVersion est toujours à 24. Réglez-le sur 26 dans android/variables.gradle et synchronisez.
Le scanner s'ouvre mais ne lit jamais le code. Essayez l'autre moteur Android, améliorez l'éclairage, utilisez la torche et limitez hint au format attendu. Les codes très petits ou endommagés demandent de rapprocher la caméra.
Permission refusée à chaque fois. L'utilisateur a refusé l'accès à la caméra précédemment. Affichez un message expliquant comment l'activer dans les paramètres du système. L'OS ne réaffichera pas le prompt.
Le scanner web ne démarre pas. La page n'est pas servie en HTTPS ou sur localhost, ou un autre onglet utilise la caméra.
Le paramètre d'orientation est ignoré sur les tablettes. Dès les applications ciblant Android SDK 36, Android 16 ignore les orientations fixes sur les grands écrans. Utilisez ADAPTIVE.
Déployez vos correctifs sans attendre la validation
Une fois le scanner en ligne, la plupart des modifications, comme de nouvelles règles de validation, des ajustements d’interface ou un autre appel backend, sont des code web. les live updates Capgo push those to installed apps within minutes, while plugin upgrades and Info.plist les modifications passent toujours par les stores. Si vous avez besoin d’un nouveau build natif sans configuration Mac locale, Build Capgo génère des binaires iOS et Android signés dans le cloud.
Récapitulatif
Un scanner de codes-barres Ionic sur Capacitor 8 nécessite un plugin officiel, deux modifications de configuration native (Android minSdkVersion 26 et la chaîne d'utilisation de la caméra iOS), et un seul scanBarcode appel. Restreignez les formats lorsque c'est possible, considérez les annulations et les erreurs de permission comme des résultats normaux, validez tout ce que vous scannez, et utilisez un aperçu de la caméra ou DataWedge lorsque le flux en une seule passe ne suffit pas.