Untuk membuat barcode scanner Ionic dengan Capacitor, buat aplikasi Ionic dengan integrasi Capacitor, lalu instal resmi @capacitor/barcode-scanner plugin resmi, naikkan minSdkVersion ke 26, tambahkan deskripsi penggunaan kamera iOS, dan panggil CapacitorBarcodeScanner.scanBarcode() dari sebuah tombol. Plugin ini membuka scanner native layar penuh di iOS dan Android, scanner browser di web, dan mengembalikan teks serta format hasil decode.
Tutorial ini membangun aplikasi scanner yang berfungsi di Ionic Angular pada Capacitor 8, lalu membahas filter format, error handling, engine scanning Android, continuous scanning, scanner hardware enterprise, dan kegagalan umum.
Yang akan Anda buat
Aplikasi Ionic satu halaman dengan tombol scan yang:
- membuka scanner kamera,
- menampilkan setiap hasil beserta formatnya,
- memungkinkan Anda menyalin atau membuka nilai hasil scan,
- menangani error pembatalan dan izin dengan rapi.
code menggunakan Angular standalone components. Pemanggilan plugin-nya sama saja di React atau Vue, hanya sintaks template-nya yang berbeda.
Prerequisites
- Node.js LTS dan Bun.
- Untuk Android: Android Studio dengan SDK serta perangkat atau emulator yang memiliki kamera.
- Untuk iOS: Mac dengan Xcode 26, yang diwajibkan oleh App Store Connect untuk upload sejak April 2026.
- Perangkat fisik adalah pilihan terbaik. Emulator bisa memindai dengan kamera virtual, tetapi pencahayaan dan fokus asli sangat penting untuk barcode.
Langkah 1: Buat aplikasi Ionic
bunx @ionic/cli start barcode-scanner blank --type=angular-standalone --capacitor
cd barcode-scanner
Ionic CLI membuat kerangka aplikasi Angular dengan @capacitor/core dan @capacitor/cli sudah terpasang. Periksa capacitor.config.ts: appId harus berupa ID reverse-domain Anda, misalnya com.example.scanner, dan webDir harus sesuai dengan output build (www untuk Ionic Angular).
Build sekali agar platform native memiliki sesuatu untuk disalin:
bun run build
Langkah 2: Tambahkan Android dan iOS
bun add @capacitor/android @capacitor/ios
bunx cap add android
bunx cap add ios
Capacitor 8 membuat proyek iOS baru dengan Swift Package Manager. Jika Anda sedang meng-upgrade aplikasi lama, ikuti panduan upgrade Capacitor 8 pertama.
Langkah 3: Instal plugin barcode scanner
bun add @capacitor/barcode-scanner
bunx cap sync
@capacitor/barcode-scanner adalah scanner resmi dari tim Capacitor. Versi 3.x mendukung Capacitor 8.
Konfigurasi Android
Plugin ini membutuhkan Android API 26 atau lebih tinggi. Capacitor 8 secara default 24, jadi edit android/variables.gradle:
ext {
minSdkVersion = 26
// keep the other values Capacitor generated
}
Di Android, Anda bisa memilih mesin decoding per scan: ZXing (default, mendukung semua format yang terdaftar di plugin) atau Google ML Kit (cepat untuk kode yang sulit, tapi tidak mendukung MaxiCode, RSS-14, RSS Expanded, atau ekstensi UPC/EAN).
Konfigurasi iOS
Tambahkan deskripsi penggunaan kamera ke ios/App/App/Info.plist:
<key>NSCameraUsageDescription</key>
<string>The camera is used to scan barcodes and QR codes.</string>
Tanpa ini, iOS akan mengakhiri aplikasi saat akses kamera pertama kali. Tulis kalimatnya untuk pengguna, karena App Review juga membacanya. iOS menggunakan framework Vision dari Apple, yang mendukung semua format terdaftar kecuali MaxiCode dan ekstensi UPC/EAN, serta tidak membedakan UPC-A dari EAN-13.
Langkah 4: Buat scanner service
Menaruh pemanggilan plugin dalam sebuah service membuatnya mudah dipakai ulang dan diuji.
// 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();
}
}
Plugin mengembalikan error dengan kode dalam format OS-PLUG-BARC-XXXX. Di iOS, 0006 berarti user membatalkan dan 0007 berarti akses kamera ditolak. Anggap pembatalan sebagai hasil normal, bukan error. Log code mentah di Android saat testing untuk memastikan mapping yang sama di perangkat target Anda.
Panduan format di dalam hasil adalah nilai enum numerik. CapacitorBarcodeScannerTypeHint[format] mengubahnya kembali menjadi nama seperti QR_CODE atau EAN_13.
Langkah 5: Buat halamannya
// 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>
Langkah 6: Jalankan di perangkat
bun run build
bunx cap sync
bunx cap run android
bunx cap run ios
Untuk versi web, bun run start menjalankan aplikasi di localhost, tempat browser mengizinkan akses kamera. Melalui IP LAN, Anda memerlukan HTTPS.
Untuk iterasi lebih cepat, gunakan live reload di perangkat: bunx cap run android --livereload --external memuat ulang aplikasi setiap kali Anda menyimpan.
Format yang didukung
| Format | Android ZXing | Android ML Kit | iOS |
|---|---|---|---|
| QR Code, Aztec, Data Matrix, PDF417 | Ya | Ya | Ya |
| EAN-13, EAN-8, UPC-A, UPC-E | Ya | Ya | Ya (UPC-A dan EAN-13 diperlakukan sama) |
| Code 128, Code 39, Code 93, Codabar, ITF | Ya | Ya | Ya |
| RSS-14, RSS Expanded | Ya | Tidak | Ya |
| MaxiCode | Ya | Tidak | Tidak |
| Ekstensi UPC/EAN | Ya | Tidak | Tidak |
If you pass an unsupported format as hint, plugin akan kembali ke pemindaian format apa pun.
Menangani nilai hasil scan dengan aman
Barcode adalah input yang tidak terpercaya. Sebelum memprosesnya:
- Validasi URL sebelum membukanya, dan tampilkan domainnya ke user terlebih dahulu.
- Jangan jalankan string hasil scan sebagai code atau suntikkan ke HTML.
- Untuk kode produk, validasi check digit sebelum memanggil backend Anda.
- Batas jumlah lookup backend jika pengguna bisa memindai dengan cepat.
Selain scanner dasar
scanBarcode adalah alur one-shot layar penuh. Itu mencakup sebagian besar aplikasi. Kebutuhan lain memerlukan tool lain:
- Scanning berkelanjutan di dalam UI Anda sendiri. Jalankan feed kamera di balik web UI Anda dengan @capgo/camera-preview, ambil frame dengan
captureSample, dan decode frame-nya di JavaScript dengan library sepertizxing-wasm. - Perangkat enterprise yang tangguh. Handheld Zebra memiliki scanner hardware. @capgo/capacitor-zebra-datawedge mengelola profil DataWedge dan pemicu scan, yang lebih cepat dan andal daripada kamera pada perangkat tersebut.
- Pengambilan dokumen. Untuk struk dan formulir kertas, plugin scanner dokumen lebih cocok daripada pembaca barcode. Lihat direktori plugin.
Datang dari Cordova? Panduan barcode scanner Cordova kami menjelaskan plugin lama dan jalur migrasi.
Pemecahan Masalah
Aplikasi iOS tertutup saat pemindaian dimulai. NSCameraUsageDescription tidak ada di Info.plist.
Build Android gagal dengan error manifest merger atau minSdk. minSdkVersion masih 24. Ubah menjadi 26 di android/variables.gradle lalu sync.
Scanner terbuka tapi tidak pernah membaca code. Coba engine Android lain, perbaiki pencahayaan, gunakan torch, dan batasi hint ke format yang diharapkan. Kode yang sangat kecil atau rusak membutuhkan kamera lebih dekat.
Izin ditolak setiap kali. Pengguna sebelumnya menolak akses kamera. Tampilkan pesan yang menjelaskan cara mengaktifkannya di pengaturan sistem. OS tidak akan menampilkan prompt lagi.
Web scanner tidak dimulai. Halaman tidak disajikan melalui HTTPS atau localhost, atau tab lain sedang menggunakan kamera.
Pengaturan orientasi diabaikan di tablet. Mulai dari aplikasi yang menargetkan Android SDK 36, Android 16 mengabaikan orientasi tetap di layar besar. Gunakan ADAPTIVE.
Rilis perbaikan tanpa menunggu review
Setelah scanner aktif, sebagian besar perubahan, seperti aturan validasi baru, tweak UI, atau backend lookup yang berbeda, adalah web code. Live updates Capgo push perubahan itu ke aplikasi terpasang dalam hitungan menit, sementara upgrade plugin dan Info.plist perubahan tetap harus melalui store. Saat Anda butuh Native Build baru tanpa setup Mac lokal, Build Capgo menghasilkan binary iOS dan Android yang sudah ditandatangani di cloud.
Kesimpulan
Ionic barcode scanner di Capacitor 8 membutuhkan satu plugin resmi, dua perubahan konfigurasi native (Android minSdkVersion 26 dan string penggunaan kamera iOS), serta satu scanBarcode call. Batasi format bila memungkinkan, anggap error cancel dan permission sebagai hasil yang normal, validasi semua yang Anda scan, dan gunakan camera preview atau DataWedge jika flow sekali jalan tidak cukup.