Feature spec
IntermediateCategory
scanning-devices
Build with AI
3–6 hours with Lovable or FlutterFlow
Custom build
3–5 days custom dev
Running cost
$0/mo (scanning only) · $25–50/mo at 10K users with Supabase Pro
Works on
Everything it takes to ship a Barcode Scanner — parts, prompts, and real costs.
A barcode scanner needs three pieces: camera access, a decoding engine (html5-qrcode for web, ML Kit for native mobile), and a handler that does something useful with the result. With Lovable or V0 you can ship a working scanner in 3–6 hours for $0/month — the decoding libraries are free and run on-device. Costs only appear if you add a third-party product lookup API to identify unknown retail barcodes.
What a Barcode Scanner Feature Actually Is
A barcode scanner turns your user's camera into an input device — point at a barcode or QR code, and the app reads the encoded value instantly. Inventory apps use it to pull up stock items, fitness apps to log food, retail apps to compare prices, event apps to check in guests. The scanning itself is a solved problem: free libraries decode EAN-13, UPC-A, Code 128, and QR formats in the browser without any server round-trip. The real product decisions are what happens after the scan — lookup, validation, history, and what to do when the camera can't read the code at all.
What users consider table stakes in 2026
- Scan completes in under one second with a visible viewfinder overlay showing the alignment target
- Haptic vibration or audible beep confirms a successful read before any screen transition
- Continuous scanning mode — no tap required between codes, viewfinder stays open
- Duplicate protection — holding the camera on the same code for 5 seconds doesn't create duplicate records
- Torch (flashlight) toggle for scanning in low-light environments like warehouses
- Manual entry fallback so users can type a barcode when a label is damaged or too small to scan
Anatomy of the Feature
Six components, four of which AI tools generate correctly on the first prompt. The camera access layer and barcode format configuration are where first builds actually fail.
Camera access layer
UIRequests camera permission via the browser's getUserMedia() API on web (html5-qrcode library) or uses native ML Kit BarcodeScanning on Android and AVFoundation VNDetectBarcodesRequest on iOS. Renders the live video stream in a canvas element or native SurfaceView with the viewfinder overlay composited on top.
Note: Requires HTTPS — camera access silently fails on http:// URLs and inside most preview iframes including Lovable and V0. Always test on the published URL.
Barcode detection engine
BackendReads frames from the video stream at approximately 10fps and decodes barcodes. Web standard: html5-qrcode v2 (wraps ZXing) handles QR plus all major retail formats. Native mobile: Google ML Kit BarcodeScanning runs on-device with hardware acceleration, producing results noticeably faster than the web counterpart. Detection loop fires the success callback on each decoded frame.
Note: Always configure an explicit format list — html5-qrcode defaults to QR-only. Pass [Html5QrcodeSupportedFormats.EAN_13, UPC_A, CODE_128, QR_CODE] to enable retail scanning.
Scan result handler
BackendReceives the decoded string, deduplicates consecutive reads using a 500ms cooldown (store last value + timestamp, reject repeats within the window), validates the format, and routes the result — look up a product, check in a guest, add inventory. Emits a structured event containing barcode value, format string, and timestamp.
Note: This is business logic unique to your app. Be explicit in your prompt about what should happen after a successful scan — AI tools default to just displaying the value.
Viewfinder overlay
UISVG or Canvas overlay drawn on top of the camera preview showing a focus rectangle, animated scanning line, and current format label. Built with React or Flutter's CustomPainter. The overlay communicates to the user where to aim and signals when a scan is in progress.
Note: Keep the decode region tight: cropping detection to the overlay box area improves speed and battery life significantly.
Scan history log
DataSupabase table scan_events storing user_id, barcode_value, barcode_format, context_id, optional metadata JSONB, and scanned_at. Powers the history screen, analytics dashboards, and cross-session duplicate detection. Indexed on (user_id, scanned_at DESC) for fast history queries.
Note: Row-level security ensures each user can only read and insert their own scan rows — AI tools sometimes generate the table without enabling RLS.
Product/asset lookup service
ServiceOptional: translates a scanned EAN or UPC into a product name, image, and metadata. A Supabase Edge Function queries an internal products table or calls Open Food Facts REST API (free, no auth) for food items, or a paid catalog API for general retail. Results cached in a local products table to avoid repeat API calls.
Note: Skip entirely if your app scans its own codes — inventory labels, event tickets, and access passes encode an ID you resolve against your own database.
The data model
Two tables: a scan log for audit history and a products cache for lookup results. Run this in the Supabase SQL editor — it creates both tables with RLS policies and the index needed for fast history queries.
1create table public.scan_events (2 id uuid primary key default gen_random_uuid(),3 user_id uuid references auth.users(id) on delete cascade not null,4 barcode_value text not null,5 barcode_format text not null,6 context_id text,7 metadata jsonb,8 scanned_at timestamptz not null default now()9);1011alter table public.scan_events enable row level security;1213create policy "Users can view own scan events"14 on public.scan_events for select15 using (auth.uid() = user_id);1617create policy "Users can insert own scan events"18 on public.scan_events for insert19 with check (auth.uid() = user_id);2021create index scan_events_user_recent_idx22 on public.scan_events (user_id, scanned_at desc);2324create table public.products (25 barcode text primary key,26 name text,27 description text,28 price numeric(10,2),29 image_url text,30 fetched_at timestamptz not null default now()31);3233alter table public.products enable row level security;3435create policy "Authenticated users can read products"36 on public.products for select37 to authenticated38 using (true);3940create policy "Service role can upsert products"41 on public.products for all42 to service_role43 using (true);Heads up: The products table acts as a lookup cache — Edge Functions write to it using the service role key, while authenticated users can read. Never expose the service role key to the browser.
Build it — pick your path
Each tool fits this feature differently. Switch tabs to compare fit, time, the exact steps, and a copy-paste prompt.
Best path for mobile: FlutterFlow's native Scan Barcode/QR Action wraps mobile_scanner (which uses ML Kit on Android and AVFoundation on iOS) — point-and-click setup with hardware-accelerated scanning.
Step by step
- 1Add a Scan Barcode/QR Action to your scan button in the Action editor — choose 'Scan Barcode' from the Camera section
- 2Store the scan result in a page state variable (type: String); bind it to a Text widget to display the decoded value
- 3Add a Supabase Backend Action after the scan action to insert a row into scan_events with user_id (from auth), barcode_value, barcode_format, and scanned_at
- 4Enable camera permissions in Settings → Permissions → iOS Permissions: add NSCameraUsageDescription with a descriptive message; do the same for Android
- 5Test on a physical iOS or Android device via the FlutterFlow mobile app — the browser preview cannot scan barcodes
Where this path bites
- The built-in Scan Barcode action is one-shot — it closes the camera after the first successful scan; continuous scanning mode requires a Custom Action with a MobileScannerController stream listener
- Torch toggle is not exposed by the built-in scanner action — it requires a one-line Custom Action calling MobileScannerController.toggleTorch()
- FlutterFlow's Flutter Web output for the scanner widget is unreliable — use FlutterFlow only for iOS and Android targets; add a manual entry fallback page for web
Third-party services you'll need
Scanning itself is free — the decode libraries run on-device. You only pay if you need to identify unknown products from their barcode value:
| Service | What it does | Free tier | Paid from |
|---|---|---|---|
| html5-qrcode | Client-side barcode decoding for web apps (EAN-13, UPC-A, Code 128, QR, and more) | Open source, fully free | Free |
| Google ML Kit Barcode Scanning | On-device barcode decoding for Android and iOS Flutter apps — faster and more accurate than web libraries | Free, runs entirely on-device | Free |
| Open Food Facts API | Product lookup by EAN-13/UPC-A for food and cosmetic items — name, image, nutrition data | Fully free, no auth required | Free (donation-supported) |
| Supabase | scan_events table, products cache, RLS, and Edge Functions for lookup | Free tier: 2 projects, 500MB DB, 500K Edge Function invocations/mo | Pro $25/mo (8GB DB, unlimited projects) |
Swipe the table sideways to see pricing.
What it costs to run
Drag through the tiers to see how your monthly bill scales with users — no surprises later.
Estimated monthly running cost
html5-qrcode and ML Kit run on-device at no cost. Supabase free tier easily handles 100 users' scan history and lookup cache. Open Food Facts is free.
Estimates use verified 2026 pricing and assume typical usage per user. Your real bill depends on activity, storage, and third-party plans.
What breaks when AI tools build this
The failures people actually hit on their first build — the symptom, why it happens, and the exact fix.
Camera blocked in Lovable and V0 preview iframes
Symptom: Both Lovable and V0 run their previews inside sandboxed iframes that apply a restrictive permissions policy blocking getUserMedia(). The scanner component appears to render correctly in the editor, but the camera feed is black and no scan events fire. This is not a bug in the generated code.
Fix: Always test barcode scanning on the published or deployed HTTPS URL, not in the editor preview. In Lovable, publish the project and open the live URL on your phone. In V0, deploy to Vercel via the Git panel first.
Duplicate scan events — same barcode inserts hundreds of rows
Symptom: html5-qrcode and mobile_scanner fire the success callback on every video frame where the barcode is detected — typically 10+ times per second. Without a cooldown, one real scan inserts dozens of rows into scan_events and triggers the success toast repeatedly, producing a terrible user experience.
Fix: Implement a client-side cooldown: store the last scanned barcode_value and the timestamp of that scan in a ref or state variable. On each callback, reject new events where the value matches and less than 500ms has elapsed since the last accepted scan.
iOS Safari requires HTTPS — common on staging environments
Symptom: getUserMedia() is blocked on iOS Safari over HTTP, including on staging environments using self-signed certificates or http:// localhost tunnels. The camera permission dialog never appears and the scanner silently fails.
Fix: Ensure your deployed URL uses HTTPS. Vercel and Lovable publish URLs are HTTPS by default. For local development on iOS, use a tool like ngrok to expose localhost over HTTPS.
Barcode format mismatch — AI tools default to QR only
Symptom: When prompted to build a barcode scanner, AI tools frequently configure html5-qrcode with only QR code support, silently ignoring EAN-13 and UPC-A retail barcodes. Users try to scan a product barcode and nothing happens — there is no error, the scanner just doesn't trigger.
Fix: In your prompt, explicitly list every format you need as an array: [Html5QrcodeSupportedFormats.EAN_13, Html5QrcodeSupportedFormats.UPC_A, Html5QrcodeSupportedFormats.CODE_128, Html5QrcodeSupportedFormats.QR_CODE]. Pass this array to the formatsToSupport option in the scanner config.
FlutterFlow web scanner crashes on Flutter Web output
Symptom: FlutterFlow's mobile_scanner package does not compile correctly for Flutter Web. The scanner widget crashes on load in web mode with a plugin missing exception, leaving users on web with a broken screen.
Fix: Use FlutterFlow's barcode scanner only for iOS and Android deployment targets. For users on web, add a conditional visibility check (Platform.isWeb) and show a manual barcode entry text field as the fallback on web.
Best practices
Restrict the decode format list to only the formats your app actually uses — scanning EAN-13 and QR only roughly doubles detection speed compared to enabling all formats
Give haptic feedback (navigator.vibrate(200) on web, HapticFeedback.mediumImpact() on Flutter) the instant a scan succeeds, before any network call, so the user knows the scan was captured
Cache product lookup results in your own products table — the second user scanning the same barcode should never hit an external API
Always ship a manual entry fallback input below the scanner — damaged, tiny, or curved labels are a daily reality in inventory and field workflows
Stop the camera stream when the scanner component unmounts — a stuck camera indicator in the browser toolbar destroys user trust and wastes battery
Log scan failures (barcode format, time spent scanning, error type) so you can see whether users struggle before they submit support tickets
Write NSCameraUsageDescription in FlutterFlow under Settings → Permissions with a specific reason — 'Camera is used to scan product barcodes for inventory tracking' — generic descriptions lead to App Store rejection
When You Need Custom Development
AI tools comfortably cover scan-and-record features for everyday volumes. Specific workloads consistently outgrow what Lovable, V0, or FlutterFlow can produce:
- Staff scan continuously for hours (warehouse, POS, field inspection) — you need native ML Kit performance, offline scan queues, and hardware Bluetooth or USB HID scanner support that web apps cannot provide
- Scans must work with zero connectivity and sync later without data loss or duplicates — true offline-first requires a local SQLite outbox that AI tools cannot reliably wire
- You need to decode damaged, curved, or poorly printed labels where free libraries plateau and evaluating commercial SDKs (Scandit-class) is necessary
- The scanner feeds a multi-step business workflow — picking, packing, stock reconciliation — where a mis-scan costs real money and requires custom error recovery logic
RapidDev builds these features for production
Real auth, database, and payments — fixed price, delivered in weeks. You own 100% of the code.
Frequently asked questions
Does barcode scanning work on mobile browsers without a native app?
Yes. html5-qrcode decodes EAN-13, UPC-A, Code 128, and QR codes through the browser camera on both iOS Safari (14.5+) and Chrome Android. Native apps win on scan speed and battery life for continuous-scanning workloads, but for occasional scan-to-look-up flows a web app works well.
What barcode formats does html5-qrcode support?
html5-qrcode supports QR Code, EAN-13, EAN-8, UPC-A, UPC-E, Code 39, Code 93, Code 128, ITF, Codabar, RSS-14, and PDF-417 among others. You must explicitly list the formats you want in the formatsToSupport config array — the default is QR-only, which silently ignores retail barcodes.
How do I stop duplicate scans from being saved to the database?
Store the last successfully scanned barcode value and the time it was accepted in a ref. On each decode callback, check whether the incoming value matches the stored value and whether the timestamp is less than 500ms ago. If both conditions are true, skip the database insert. This is the 500ms cooldown pattern — include it explicitly in your build prompt.
Can I scan barcodes in the Lovable or V0 preview without publishing?
No. Both preview environments run inside sandboxed iframes that block camera access via getUserMedia(). This is a browser security restriction, not a bug in your code. Always test scanning on the published or deployed HTTPS URL on a real phone.
How do I add product lookup when a barcode is scanned?
Create a Supabase Edge Function that receives the barcode value, queries your products table first (as a cache), and if not found calls the Open Food Facts API (for food items, free) or a paid catalog API. Return the product name and image to the client and cache the result in your products table so the next user scanning the same barcode doesn't hit the external API.
Is Google ML Kit barcode scanning free to use?
Yes, entirely free. ML Kit Barcode Scanning runs on-device on Android and iOS with no API calls, no quota, and no per-scan fee. It is bundled with Google Play Services on Android and part of the Vision framework on iOS.
How do I handle the camera permission denied state gracefully?
In html5-qrcode, catch the error callback and check for the NotAllowedError type. On denial, render a permission explainer screen explaining why the camera is needed, with a 'Try Again' button that calls Html5Qrcode.getCameras() again. In FlutterFlow, use permission_handler's Permission.camera.isPermanentlyDenied check and show an 'Open Settings' button calling openAppSettings() for users who chose 'Never Ask Again' on Android.
Can FlutterFlow build a barcode scanner for both iOS and Android?
Yes, and it is the easiest path for mobile — FlutterFlow's built-in Scan Barcode/QR action wraps mobile_scanner which uses ML Kit on Android and AVFoundation on iOS. Both platforms work with no code written. The limitation is that the built-in action is one-shot per tap; continuous scanning mode and torch control require a Custom Action.
Need this feature production-ready?
RapidDev builds a barcode scanner into real apps — auth, database, payments — at $13K–$25K.
Book a free consultation30-min call. No commitment.