universal_barcode_scanner 2.3.0
universal_barcode_scanner: ^2.3.0 copied to clipboard
Barcode and QR code scanner for Flutter, on Android, iOS, Linux, macOS, web and Windows, from one widget and one callback.
Universal Barcode Scanner Versions #
2.3.0 #
Added #
- The embedded view hands the app its camera frames, in grey, for it to
read what is not a code, such as the machine readable zone of a passport
or an identity card:
onFramereceives aScanFramewith the luminance under the scan window, at most everyframeInterval(200 ms by default) and none while reading is paused. On every platform: the camera's own frames on Android, iOS and macOS, the page's on the web, Windows and Linux. ScanFormat.nonereads no code at all, for a view that only hands its frames on.
Compatibility #
- A view without
onFrameasks the platform for nothing more and behaves as before. ScanFormathas a new value: aswitchover it that names every value without a default branch must now handleScanFormat.none.
2.2.4 #
Changed #
- The badge row carries a PayPal donation badge,
fundingpoints pub.flutter-io.cn at the same donation page, and the README ends on the other packages COMAPPS publishes. Nothing about the library changed.
2.2.3 #
Changed #
- The scanner page and the embedded view on the web, Windows and Linux draw a lighter scan window, the same in both: cut out of the dimmed surround with rounded corners and marked by four thin corner lines, and its line sweeps from edge to edge with a faint trail behind it, in three seconds, rather than glowing. In the embedded view a wide window is narrower, 72 % of the view rather than 85 %, so it stays clear of a column of buttons, and takes at most 60 % of the view's height rather than 80 %, so a low view no longer fills with it.
- The example is drawn light, and ends on an event check-in: the embedded
view reads tickets at a gate, counts the guests in, and turns away a
ticket already used or bought for another evening, with the validator.
It ships tickets to try: tapping one hands it to the gate through
scanImage, and another device can scan them with its camera. - The pub.flutter-io.cn screenshots open on a cover.
2.2.2 #
Changed #
- The README and the
ScanContentdocs match a code's content with a typed variable,case WifiContent wifi:, rather than the shortercase WifiContent(:final ssid):pattern fewer readers know. - The README links to a guided tour of the package on YouTube.
2.2.1 #
Fixed #
- The sound of a code refused in a browser was too low for a phone's speaker, and went unheard in Chrome on Android. It is now two beeps at 480 Hz, rich in overtones, rather than 330 Hz.
- On Windows, a code refused sounded like a code read: both were the
system's alert sound, the only one Flutter plays there.
beepnow plays the same tones as in a browser, made by the package and played through the system'sPlaySound.
2.2.0 #
Added #
UniversalBarcodeScanner.scanImagereads every code in an image, given as bytes: a photo, a file, an asset. No camera and no permission. ML Kit reads it on Android and Vision on iOS and macOS, turning a photo the way its EXIF says; the web, Windows and Linux read it with the scanner page's decoder.ScannerErrorCode.invalidImagesays the bytes are no image. The package picks no file itself and adds no dependency.ScanResult.contentsays what a code holds where its text follows a format phones know: a link, a Wi-Fi network, a vCard or MECARD contact, an e-mail, a phone number, a text message, a place or a calendar event, as a sealedScanContent. Read from the text alone, the same on every platform.validatoronscan,scanResult,stream,resultStreamand the embedded view decides which codes count. A code refused does not close the scanner and is not handed on:ScannerLabels.rejectedis shown over the camera, whose edge flashes red twice with two short vibrations, or on the native screens of Android, iOS and macOS, read out by a screen reader, with a lower sound than for a code read whenbeepis on, and reading goes on. A code held in front of the camera is refused once.- The web, Windows and Linux pages and the embedded view flash the edge of the camera green once for each code accepted, validator or not.
- The example reads an image the user picks with
file_picker, or one of the pictures it ships, one per kind of content, and says what each code holds. It can accept only links or only products, to show the validator, and marks the codes it refuses, checked again when the choice changes. An integration test runsscanImageon a device.
Changed #
- On iOS and macOS, the plugin's Swift package depends on
FlutterFrameworkwhere Flutter provides it, from 3.44, as Flutter now asks of plugins. An older Flutter with Swift Package Manager turned on has no such package, and builds as before. - The embedded view takes a new
continuousorvalidatorwithout restarting the camera. The view always reads on, and the widget pauses it on the first code accepted when it is not continuous.
2.1.3 #
Fixed #
- The pause button of the web, Windows and Linux scanner stops the scan line where it is, as the embedded and native views do, and resuming sets it off again from there. Reading was paused, but the line kept sweeping.
Changed #
- The buttons over the camera no longer change colour under the mouse. In Chrome the tint could stay on a button once the mouse had left it for the camera. The hand and the tooltip still show over them.
2.1.2 #
2.1.1 #
Fixed #
- On the web, Windows and Linux, a frame the decoder cannot read no longer sends every later frame to the app's thread: a worker is given up only after several unreadable frames in a row, or when it stops.
- Where the browser has a barcode detector that does not read every format asked for, the bundled reader now runs in a worker, and no longer on the app's thread.
Changed #
- Changing the formats while the scanner is open keeps the worker and the reader it compiled, rather than loading and compiling them again.
- The bundled reader lets go of its encoded copy once compiled, and decodes it natively where the browser can.
- Without
createImageBitmap, the canvas a frame is copied through keeps its size from one frame to the next. - The page the web, Windows and Linux scanners run is tested in a browser, in CI, with a camera drawn on a canvas.
2.1.0 #
Changed #
- The camera is now mirrored by default on a desktop and in a desktop
browser, where it is a webcam facing the user; not on a phone or a
tablet.
flipis abool?: passfalsefor the old behaviour.UniversalBarcodeScanner.flipsByDefaultsays whatnulldoes. - The web, Windows and Linux scanner decodes with zxing-cpp compiled to WebAssembly, through barcode-detector, in place of html5-qrcode, which has not had a release since April 2023. The browser's own detector is still used where there is one. On the web the decoding runs in a worker, off the app's thread. The WebAssembly is bundled: nothing is fetched from a CDN.
- The page drives the camera itself: a resized window or view no longer restarts it, and only the camera turns when flipped, so the scan box and the page's messages stay the right way round.
ScannerBar.cancelLabelreplaces thecancelLabelofscanandstream, which is deprecated and still wins when given. It also labels the bar's back button for a screen reader.- On Windows and Linux, a webview no longer in use is disposed rather than
left loaded with a blank page.
webview_all1.4.3 or later.
Added #
ScanResultandBarcodeFormat:scanResult,resultStreamand the embedded view'sonResultsay which symbology each code was printed in, on every platform.scan,streamandonScannedare unchanged.buttonsandbuttonsAlignmentonscan,streamand the embedded view: round buttons over the camera for the torch, pausing, each flip, the zoom and the other camera, down the right side by default. Onscanandstreamthey are drawn on the web, Windows and Linux; the native screens of Android, iOS and macOS keep their own, and atorchasked for shows the native torch button. The embedded view is drawn by Flutter everywhere, so its buttons are too.buttonStyle, aScannerButtonStyle: the size, spacing, colours, focus ring, corners and tooltips of the buttons and of the close button.- The buttons, the close button and the bar's back button work with the mouse and the keyboard: the hand over them, a tooltip saying what each one does, Tab to move between them with a ring around the one reached, Enter or Space to press it. On the web they take the pointer from the camera's frame beneath, which kept every click and move to itself.
flipVertical, to show the camera upside down.labels, aScannerLabels: the words of the buttons, for screen readers, and of the page when the camera will not start. English by default;ScannerLabels.french,.dutchand.germanare ready to use.animate, on by default: the camera fades in once its first frame is on screen, and a flip turns it over through its middle. The native embedded views now say when their first frame is there. Off when the platform asks for reduced motion.vibrateandbeep, to signal each code read.scanWindowSizeonscanandstream, as the embedded view already had.- On
ScannerController:isPaused,isTorchOnandzoom, which follow the view whoever changes it, andsetZoom. The buttons show this state. - The example shows the symbology of each code, uses every button, and lets you pick where the buttons sit, how they look and in which language the scanner speaks. It is called Universal Barcode Scanner on every platform.
2.0.1 #
- The README shows the embedded view at the top of the page, with the other two animations.
2.0.0 #
A rewrite: every platform was reworked, and the package no longer carries code or assets from the packages it started from.
Breaking changes #
| 1.x | 2.0 |
|---|---|
| Flutter 3.27 | Flutter 3.35, which the Dart 3.9 floor already required |
scanType: ScanType.barcode / .qr / .defaultMode |
scanWindow: ScanWindow.wide / .square / .square, or ScanWindow.none for no window |
isShowFlashIcon |
showTorchButton |
cancelButtonText |
cancelLabel |
barcodeAppBar: BarcodeAppBar(appBarTitle:, enableBackButton:, backButtonIcon:) |
bar: ScannerBar(title:, showBackButton:, backIcon:), back button on by default |
onBarcodeViewCreated: (BarcodeViewController c) {} |
onCreated: (ScannerController c) {} |
scaleWidth, scaleHeight |
scanWindowSize, in logical pixels |
scan(scanDelay:), UniversalBarcodeScanner(onClose:) |
Removed |
A camera that could not be used left scan waiting |
scan throws a ScannerException; stream emits one and closes |
A code reading -1 or -2 was taken for a cancel |
Every payload is a code |
scanDelay delayed a single scan's result |
It is the least time between two codes of a continuous scan |
An embedded view that is not continuous kept reporting |
It pauses on the first code until resumeScanning() |
BarcodeViewController(id) could be built by hand |
ScannerController is abstract: the widget hands one to onCreated |
Added #
- The embedded view on every platform: iOS and macOS as native views, and the web, Windows and Linux as the bundled page in a view the size of the widget. It was Android only, although the README promised iOS.
ScannerException, withpermissionDenied,cameraUnavailableandalreadyActive, instead of dialogs and futures that never completed.ScanWindow.none: nothing drawn over the camera, the whole frame read.onErroron the embedded view, andScannerController.dispose.- A close button over the web, Windows and Linux scanner when there is no bar, and Escape to close it.
Fixed #
- A code held in front of the camera is reported once, and again after a second out of sight, with each code followed on its own. Continuous scans used to repeat it on every frame.
- Two scans in a row no longer interfere: every scan carries a session, and the native scanner closes as soon as its route is popped.
streamreturns every code on the web, Windows and Linux, andstream(context).firstcloses the scanner.- The web, Windows and Linux scanner decodes at the camera's resolution rather than at the size it is shown: small barcodes that never read now do.
- iOS: no crash on launch under the scene life cycle, no crash on a late code, and a scanner closed while appearing or covered by another screen is dismissed.
- Android: the scanner's buttons sit above the navigation bar on Android 15, a rotation no longer fails the permission request, and a frame in flight at closing no longer crashes.
- Settings that some platforms ignored now apply everywhere:
scanFormat,cameraFaceandscanDelayon the web and desktop, and all of them in the Android embedded view. - An app created by Flutter 3.47 builds: the Android plugin compiles against API 36, as its dependencies require.
Changed #
- Android is written in Kotlin, compiled with AGP 9's built-in Kotlin support where it is on.
- Android analyses frames at about 1280 by 960, and iOS and macOS start the camera off the main thread.
- Restricting
scanFormatmakes every platform faster. - The icons are drawn for the package, and
LICENSEis MIT under its author alone. html5-qrcode's Apache 2.0 licence now ships next to it.
1.6.3 #
Changed #
- The README carries its build badge again, pointed at the branch the repository actually builds from.
- The example test looked for a card the list had never built. It asked for a
title the rewritten example no longer uses, and the third card sits below the
fold of the test window, where a
ListViewdoes not build it. The test scrolls to it now.
1.6.1 #
Fixed #
- The web scanner said nothing when the camera did not start. The rejection
from
getUserMediawas swallowed, so a refused permission, a machine with no camera and a camera held by another application all gave the same black rectangle, with the reason only in the console. The page now says which of the three it was and what to do about it. - The scanner bar title was underlined in yellow on the web.
ScannerChromeis built without Material, so aTextStylethere has to setdecorationas well: setting only the colour, the size and the weight left the ambient fallback style and its double underline in place.
Changed #
- The example is one screen rather than three buttons: what the last scan returned with its length and a count, a card per mode saying what that mode hands back, and the embedded view marked mobile only on the web, where the scanner runs in a frame of its own. It also says that a browser needs permission and HTTPS before any of it works.
homepagepoints at the package's card on comapps.web.app, where the example runs in the browser.
1.6.0 #
Changed #
- Windows and Linux keep the scanner webview between two scans instead of building one each time. Reopening the scanner was paying for the engine, the page, its script and a fresh camera request every single time, which is why the second scan was as slow as the first while a browser was instant. The webview is let go after a minute without a scanner on screen.
- The bundled page can be resumed. Stopping it and delivering a code both latched it shut, so a page kept for reuse would have come back dead.
1.5.2 #
Fixed #
backgroundColornow reaches the web and desktop scanner. The page those two load paints its own background and covers the whole route, so setting the colour behind it changed nothing: it is handed to the page itself, the way the scan line colour already was.backgroundColorreaches the Windows and Linux page at all. It stopped at the delegation from the shared page to the desktop one.
1.5.1 #
Added #
scanandstreamtake abackgroundColor, which is what the page paints behind the camera. 1.5.0 replaced aScaffoldwith a black box, so a scanner opened inside a light application turned its own background black with no way to say otherwise. Black stays the default, since that is what a camera page usually wants.
1.5.0 #
Changed #
- The package no longer imports
package:flutter/material.dartanywhere. Every file is onpackage:flutter/widgets.dart, the layer bothmaterial.dartand thematerial_uipackage are built on, so the scanner composes with either and imposes neither on the app that embeds it. - The scanner bar is drawn by the package rather than taken from Material. It
is black with white text, which suits a camera, and
BarcodeAppBargainsbackgroundColorandforegroundColorto say otherwise. An application that passed aBarcodeAppBarhad a bar coloured by its own theme until now, so this one looks different until those two are set. - The back button has no ripple. Drawing one would mean picking a design system, which is what this release is getting rid of.
- The scanner route is a
PageRouteBuilderrather than aMaterialPageRoute, so it fades in instead of using the platform's own page transition. - While the native scanner opens, the route behind it is black with a plain
spinner rather than a
Scaffoldwith a Material one.
1.4.0 #
Changed #
- Android scans with CameraX and ML Kit. Google Mobile Vision, the
play-services-visionlibrary the scanner read barcodes with until now, has been deprecated since 2021 and receives neither fixes nor model updates. The Dart API is unchanged. - The ML Kit model is bundled rather than fetched, so the scanner reads a code on a device with no Google Play services and downloads nothing before the first scan.
minSdkVersionmoves from 16 to 21, the floor CameraX and ML Kit share. It is below the one Flutter itself sets, so an app on a current Flutter has nothing to change.- A pinch follows the gesture instead of applying its scale once the gesture ends.
- A tap focuses the camera where it landed.
Fixed #
- The embedded view draws the camera inside the widget. Its preview is a
TextureView, which a Flutter platform view composes into its texture; aSurfaceViewis drawn straight to the window instead, beside the widget rather than in it.
Removed #
- The camera pipeline Mobile Vision came with:
CameraSource,CameraSourcePreview, the graphic overlay and the barcode trackers, about 1700 lines that CameraX replaces. - The box drawn around a detected code, and the tap that chose between several. The screen returns on the first code read, so the box was on screen for a frame.
android.permission.FLASHLIGHTandandroid:largeHeapfrom the plugin manifest. The torch goes through CameraX, and the heap belongs to the app rather than to one of its plugins.jcenter(), read-only since 2021, and thematerialandlegacy-support-v4dependencies that a single Snackbar held on to.
1.3.0 #
Added #
- Swift Package Manager support on iOS and macOS, alongside the existing CocoaPods podspecs. Both build systems work; an app picks whichever it has enabled. This was the only thing left costing the package points on pub.flutter-io.cn, which reported the plugin as CocoaPods-only.
Removed #
- The Objective-C shim on iOS. It forwarded registration to the Swift plugin, but nothing called it: the generated registrant has always referenced the Swift class directly. Swift Package Manager also refuses two languages in one target, so a dead file was standing in the way of a working one.
Changed #
- The Apple sources moved to the layout Swift Package Manager expects,
<platform>/universal_barcode_scanner/Sources/universal_barcode_scanner/, and the podspecs point at the new paths. Nothing changes for consumers. - The iOS icons are looked up through the bundle that the running build system provides,
Bundle.moduleunder Swift Package Manager and the class bundle under CocoaPods, rather than assuming the latter.
1.2.0 #
Added #
- A sweeping line over the scan square on web, Windows and Linux, so those three look like the
native scanners rather than a bare camera feed. It hangs inside the scan region the page already
draws, so it tracks the square even when the library shrinks it to fit the video, and it holds
still under
prefers-reduced-motion. lineColornow reaches web, Windows and Linux, where it used to be ignored. The web host passes it in the page's query string; the desktop hosts set it once the page is up.
Fixed #
- On Windows and Linux the webview no longer fills the whole window with a stretched camera. The framing moved into the scanner page, where it belongs: the library derives the video and the whole shading overlay from the width it measures on its own container, so sizing the view from the host to a box the page did not lay out for stretched everything, turning the scan square into a narrow sliver. The page caps and centres itself, and the view simply fills what it is given.
- The app bar title was forced to white on web and left to the theme everywhere else, so the same
scanner showed a white title in a browser and a dark one on Windows. The plugin imposes no colour
now: the app bar follows the host's
AppBarTheme, as it should. - Resizing the window left the scanner on stale geometry. The library measures its container once, when starting, and sizes the video and the shading overlay from it; it never measures again, so a resized window showed the two at different sizes and offsets. The page watches its container now and restarts the scanner, debounced, when the width really changes.
- The scan square is derived from the viewfinder instead of being fixed at 280 pixels. The library drops its shaded region altogether once the square is taller than the video, which is what happens on a narrow viewport: mobile web showed a bare camera, with no square and no line. The video is also centred when its ratio leaves room in the host box.
- The scanner page no longer forces an aspect ratio on the camera. Forcing one sized the video to a shape the host box did not have, which overflowed and raised scrollbars over the preview. The page follows the camera's own ratio now, and hides any rounding leftover rather than scrolling it.
1.1.0 #
Linux, and one webview for both desktops.
Added #
- Linux, through the same bundled
html5-qrcodepage Windows already used. It works because the plugin answers the page's camera permission request on the host side, which is what usually stops a webview from scanning on Linux: WebKitGTK denies a media request the embedder does not handle. Needslibwebkit2gtk-4.1-0, which most desktop installs already carry.
Changed #
- Windows and Linux now share a single implementation, on
webview_allin place ofwebview_windows. That package carries both a WebView2 and a WebKitGTK backend, and is the only Linux webview on pub.flutter-io.cn that surfaces the camera permission request instead of letting the engine deny it by default. pathis gone with it. It only existed to resolve the bundled page next to the executable, andwebview_allloads it as a Flutter asset directly. What is left iswebview_allandweb.
Fixed #
- The bundled page reached for
window.chrome.webviewand compared it to the string'undefined', which threw aTypeErroron any browser without it, Firefox included. It now feature-detects the JavaScript channel and falls back to the parent frame on the web. - The page posted the scanned code to
'*', so an embedding parent on another origin would have received it. It names its own origin as the target now.
Known issue on Windows #
Building with Visual Studio 2026 fails on error STL1011, because the webview dependency pins the
Windows Implementation Library at its 2022 release, whose headers still include
<experimental/coroutine>. It is not specific to this release: webview_windows, used up to
1.0.0+1, pins the very same version. The README carries the one-line workaround until it is bumped
upstream.
Note on 1.0.0 #
The 1.0.0 notes below claim no published Flutter webview grants camera access on Linux. That was
wrong: it held for the two packages checked at the time, not for the ecosystem. webview_all does
grant it, which is what made this release possible.
1.0.0+1 #
Add gitlab CI workfown
1.0.0 #
First release.
Barcode and QR code scanning on Android, iOS, macOS, web and Windows, from a single entry point:
UniversalBarcodeScanner.scan for one code, UniversalBarcodeScanner.stream to keep reading, and
the widget itself to embed the camera on Android and iOS.
macOS #
macOS is new, and native rather than a webview: AVCaptureSession for the camera and Vision's
VNDetectBarcodesRequest for the decoding, which is the only path Apple offers there since
AVCaptureMetadataOutput reads no barcodes on the Mac. It needs macOS 10.15, an
NSCameraUsageDescription, and the com.apple.security.device.camera entitlement, all covered in
the README. The embedded view stays Android and iOS only.
Linux is not covered. Neither published Linux webview lets a page reach the camera, since
desktop_webview_window does not connect WebKitGTK's permission-request signal and webview_cef
implements no CefPermissionHandler, and both engines deny by default. The native route is no
better, camera_linux having stood at 0.0.8 since 2023. The scanner says so on Linux instead of
raising a MissingPluginException.
Dependencies #
permission_handler is gone. It was pulled in for a single camera status read on Windows, and in
return every app depending on this plugin inherited its Android module, whose current release
forces compileSdk 37. The Windows scanner now simply asks through the webview's own permission
prompt and remembers the answer for the scan. What is left is webview_windows, path and web.
Lineage #
The package is a derivative of simple_barcode_scanner by Kunchok Tashi, which embeds the Android and iOS scanner of flutter_barcode_scanner by Amol Gangadhare. Both are MIT, and their copyright notices are kept in LICENSE. Coming from either of them, the API is not the same: see the migration table in the README.
Fixed since the code it derives from #
- On web the scanner registered a new platform view, built a new iframe and opened a new
messagelistener on every rebuild, and never cancelled any of them. It now sets all three up once and tears them down with the widget. - On web a message from any origin was taken for a scan, so an embedded frame or a browser extension could feed the app a barcode of its choosing. Only messages from the page's own origin are read now.
- On web the height of the scanner was decided from the viewport width, which squashed the preview on a wide, short window.
- On Windows the webview was reinitialised and its message stream resubscribed on every rebuild, which delivered a scan several times over.
- On Windows the camera permission was read asynchronously from
build, so the result never arrived in time and the permission dialog was shown even when permission had already been granted. - On Windows the webview controller was disposed twice when leaving through the app bar.
UniversalBarcodeScanner.streamnow closes its stream however the route is left, including a system back gesture. It used to leak the controller unless the app bar button was used.- Linux says it is unsupported instead of raising a
MissingPluginException. - Cancelling a single scan returned the raw sentinel
'-1'to the caller, which the documentation described asnull. It is nownull, and'-1'no longer leaks into a continuous stream either. - The native scanner kept a stream field it never assigned, so the cached broadcast stream it was meant to reuse was rebuilt on every call.
Renamed from the code it derives from #
The plugin used to register itself under another package's namespace, which made it impossible to
depend on both in one app: two AARs declaring com.amolg.flutterbarcodescanner fail to merge, and
two pods declaring SwiftFlutterBarcodeScannerPlugin fail to link. Everything now sits under
be.comapps.universal_barcode_scanner, and the method channels, event channel and platform view
type are named after this package.
