Flutter ZXing
Flutter ZXing is a high-performance Flutter plugin for scanning and generating QR codes and barcodes. Built on the powerful ZXing C++ library, it provides fast and reliable barcode processing capabilities for Flutter applications. Whether you need to scan barcodes from the camera or generate custom QR codes, Flutter ZXing makes it seamless and efficient.
Table of Contents
- Flutter ZXing
Demo Screenshots
Left: Barcode Scanner, Right: QR Code Creator
Features
- Scan QR codes and barcodes from the camera stream, an image file, or a URL. On desktop the camera needs
camera_desktop. - Scan multiple barcodes at once from the camera stream, an image file, or a URL.
- Generate QR codes with customizable content and size.
- Return the position points of the scanned barcode.
- Customizable scanner frame size and color, and the ability to enable or disable features like torch and pinch to zoom.
Supported Formats
| Linear product | Linear industrial | Matrix |
|---|---|---|
| UPC-A | Code 39 | QR Code |
| UPC-E | Code 93 | Micro QR Code ᴿ |
| EAN-8 | Code 128 | rMQR Code ᴿ |
| EAN-13 | Codabar | Aztec |
| DataBar | DataBar Expanded | DataMatrix |
| DataBar Limited ᴿ | ITF | PDF417 |
| Telepen ᴿ | MicroPDF417 ᴿ | |
| DX Film Edge ᴿ | MaxiCode ᴿ |
ᴿ read only — these can be scanned but not generated.
Powered by zxing-cpp v3.1.1.
Supported Platforms
| Platform | Status | Notes |
|---|---|---|
| Android | ✅ Fully Supported | Minimum API level 23 (Android 6.0) |
| iOS | ✅ Fully Supported | Minimum iOS 13.0 |
| MacOS | ⚠️ Beta | Minimum macOS 10.15, camera needs camera_desktop |
| Linux | ⚠️ Beta | Camera needs camera_desktop |
| Windows | ⚠️ Beta | Without Camera support |
| Web | ❌ Not Supported | Dart FFI is not available on the web |
Note: Flutter ZXing relies on the Dart FFI feature, making it unsupported on the web.
ZXScanner
ZXScanner is a free QR code and barcode scanner app for Android and iOS. It is built using Flutter and the flutter_zxing plugin.
Features
- Fast and reliable QR code and barcode scanning.
- Built-in support for multiple barcode formats.
- Fully open-source and customizable.
Try ZXScanner
To learn more or contribute, visit the ZXScanner repository.
Getting Started
Cloning the flutter_zxing project
To clone the flutter_zxing project from Github which includes submodules, use the following command:
git clone --recursive https://github.com/khoren93/flutter_zxing.git
Installing dependencies
Use Melos to install the dependencies of the flutter_zxing project. Melos is a tool that helps you manage multiple Dart packages in a single repository. To install Melos, use the following command:
flutter pub global activate melos
To install the dependencies of the flutter_zxing project, use the following command:
melos bootstrap
To allow the building on iOS and MacOS, you need to run the following command:
./scripts/update_ios_macos_src.sh
To run the integration tests:
cd example
flutter test integration_test
On macOS, Linux and Windows, run one file at a time
(flutter test integration_test/ffi_test.dart -d macos). A desktop app cannot be
started twice within one flutter test run. scripts/run_integration_tests.sh <device> runs every file that way, as CI does.
Now you can run the flutter_zxing example app on your device or emulator.
Use with dependency_overrides
If you want to use a forked version of the flutter_zxing library in your project, you can specify it using dependency_overrides in your pubspec.yaml. However, be aware that flutter_zxing relies on the ZXing C++ code included as a git submodule. When using dependency_overrides with a git repository, these submodules are not automatically included, and the update_ios_macos_src.sh script is not run, which can lead to errors, especially on iOS.
Your project might build but encounter runtime errors due to missing library exports that appear as an error like this: flutter: type 'ArgumentError' is not a subtype of type 'Code' in type cast
Recommended Approach: Using a Git Submodule
To ensure all necessary files are present, it's recommended to add your forked repository as a git submodule, initialize the submodules recursively, and reference it using a local path. Follow these steps:
- In your project add your fork as a submodule and initialize it recursively.
git submodule add https://github.com/YourUsername/flutter_zxing.git flutter_zxing
git submodule update --init --recursive
- Add your submodule to your
pubspec.yamlfile as apathdependency override.
dependency_overrides:
flutter_zxing:
path: ./flutter_zxing
- Run
flutter pub getto install the dependencies. - Run the
scripts/update_ios_macos_src.shscript to update the iOS and MacOS source files. (Or add it to your own projects build process) - Run
flutter cleanto clear the build cache. - Build and run your project.
Why Not Use a Direct Git Reference?
Referencing your forked repo as a direct git reference in the depenency_overrides section of the pubspec.yaml does not include submodules or run the update_ios_macos_src.sh script. Manually running these steps in the .pub-cache directory is not practical, since the path changes with each commit.
Usage
To read barcode
flutter_zxing re-exports the camera types that appear in its own API
(CameraImage, XFile, CameraController, ...), so a single import is enough.
import 'package:flutter_zxing/flutter_zxing.dart';
// Use ReaderWidget to quickly read barcode from camera image
@override
Widget build(BuildContext context) {
return Scaffold(
body: ReaderWidget(
onScan: (result) async {
// Do something with the result
},
),
);
}
// Or use flutter_zxing plugin methods
// To read barcode from camera image directly
await zx.startCameraProcessing(); // Call this in initState
cameraController?.startImageStream((image) async {
final Code result = await zx.processCameraImage(
image,
DecodeParams(
// Maps the frame's layout onto what the decoder expects, including
// the RGBA frames `camera_desktop` reports as bgra8888 on desktop.
imageFormat: cameraImageFormat(image),
format: Format.any,
width: image.width,
height: image.height,
),
);
if (result.isValid) {
debugPrint(result.text);
}
});
zx.stopCameraProcessing(); // Call this in dispose
// To read a barcode from an XFile, a path, a URL or raw bytes.
// Every method takes a DecodeParams; the defaults are a good starting point.
XFile xFile = XFile('Your image path');
Code resultFromXFile = await zx.readBarcodeImagePath(xFile, DecodeParams());
String path = 'Your local image path';
Code resultFromPath = await zx.readBarcodeImagePathString(path, DecodeParams());
String url = 'Your remote image url';
Code resultFromUrl = await zx.readBarcodeImageUrl(url, DecodeParams());
// `readBarcode` is synchronous and takes already-decoded pixels, so the image
// dimensions and pixel format have to be described explicitly.
Uint8List bytes = Uint8List.fromList(yourImageBytes);
Code resultFromBytes = zx.readBarcode(
bytes,
DecodeParams(imageFormat: ImageFormat.rgb, width: width, height: height),
);
// The reading methods never throw: a failure is reported on the result.
if (!resultFromPath.isValid) {
debugPrint(resultFromPath.error);
}
// Every method above has a `readBarcodes...` counterpart that returns `Codes`
// with every symbol found in the image.
Codes allCodes = await zx.readBarcodesImagePath(xFile, DecodeParams());
Small or dense codes
ReaderWidget scans only the square cut-out in the middle of the frame:
cropPercent (0.5 by default) of the frame's shorter side. With the default
ResolutionPreset.high, which is 720p, the decoder gets 360×360 pixels. That is
plenty for a QR code on a phone screen, but not for a dense symbol, such as a
GS1 DataMatrix on a label, which needs a few pixels per module to decode.
For such codes, ask for more pixels, at the cost of a longer decode per frame:
ReaderWidget(
resolution: ResolutionPreset.ultraHigh, // 2160p; veryHigh is 1080p
cropPercent: 0.7,
onScan: (code) {},
)
The camera may deliver a smaller frame than the preset asks for. On Android,
ResolutionPreset.max has been seen to give a 1080p stream where ultraHigh
gave 2160p (#251). Check what arrives: code.position?.imageWidth and
imageHeight are the size of the whole frame, on failed scans as well
(onScanFailure).
Camera on desktop
Reading barcodes from images and generating them work on desktop out of the box.
Scanning from the camera needs one more step, because the camera package ships
camera implementations for Android, iOS and the web only.
Add camera_desktop to the
pubspec.yaml of your app — it implements the same camera interface, so
ReaderWidget and zx.processCameraImage then work unchanged:
dependencies:
camera_desktop: ^1.2.2
Linux additionally needs the GStreamer development packages at build time:
# Ubuntu/Debian
sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-good
Its camera currently fails to initialize above ResolutionPreset.low on Linux
(camera_desktop#8).
macOS needs camera access declared by the app: add
NSCameraUsageDescription to macos/Runner/Info.plist, and, for a sandboxed
app, com.apple.security.device.camera to both entitlements files. See
example/macos/Runner for what that looks like.
Windows is not covered yet: camera_windows has no image stream.
camera_desktop streams frames on Windows too, but that has not been verified
with this plugin.
Desktop cameras have no torch and no zoom, so ReaderWidget hides its flash
button there and pinch-to-zoom does nothing.
To create barcode
import 'dart:typed_data';
import 'package:flutter_zxing/flutter_zxing.dart';
// Use WriterWidget to quickly create barcode
@override
Widget build(BuildContext context) {
return Scaffold(
body: WriterWidget(
onSuccess: (result, bytes) {
// Do something with the result
},
onError: (error) {
// Do something with the error
},
),
);
}
// Or use FlutterZxing to create barcode directly
final Encode result = zx.encodeBarcode(
contents: 'Text to encode',
params: EncodeParams(
format: Format.qrCode,
width: 120,
height: 120,
margin: 10,
eccLevel: EccLevel.low,
),
);
if (result.isValid && result.data != null) {
// `result.data` holds one byte per pixel. Always render it with the size
// the encoder reports: when the symbol does not fit the requested box,
// zxing enlarges it, so `result.width`/`result.height` may differ from the
// width and height that were asked for.
final Uint8List encodedBytes = pngFromBytes(
result.data!,
result.width!,
result.height!,
);
// use encodedBytes as you wish, for example Image.memory(encodedBytes)
} else {
debugPrint(result.error);
}
License
MIT License. See LICENSE.