Project in Maintenance Mode Only
Since the underlying frameworks of this package, zxing for android and MTBBarcodescanner for iOS are both not longer maintaned, this plugin is no longer up to date and in maintenance mode only. Only bug fixes and minor enhancements will be considered.
I am developing a new plugin mobile_scanner that uses the latest version of MLKit for detecting barcodes and QR codes. On Android it also uses the latest version of CameraX, and on iOS the native AVFoundation for best camera performance.
QR Code Scanner
A QR code scanner that works on both iOS and Android by natively embedding the platform view within Flutter. The integration with Flutter is seamless, much better than jumping into a native Activity or a ViewController to perform the scan.
Android | |
---|---|
iOS | |
When a QR code is recognized, the text identified will be set in 'result' of type Barcode
, which contains the output text as property 'code' of type String
and scanned code type as property 'format' which is an enum BarcodeFormat
, defined in the library.
class _QRViewExampleState extends State<QRViewExample> {
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
Barcode? result;
QRViewController? controller;
// In order to get hot reload to work we need to pause the camera if the platform
// is android, or resume the camera if the platform is iOS.
@override
void reassemble() {
super.reassemble();
if (Platform.isAndroid) {
controller!.pauseCamera();
} else if (Platform.isIOS) {
controller!.resumeCamera();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Expanded(
flex: 5,
child: QRView(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
),
),
Expanded(
flex: 1,
child: Center(
child: (result != null)
? Text(
'Barcode Type: ${describeEnum(result!.format)} Data: ${result!.code}')
: Text('Scan a code'),
),
)
],
),
);
}
void _onQRViewCreated(QRViewController controller) {
this.controller = controller;
controller.scannedDataStream.listen((scanData) {
setState(() {
result = scanData;
});
});
}
@override
void dispose() {
controller?.dispose();
super.dispose();
}
}
In order to use this plugin, please update the Gradle, Kotlin and Kotlin Gradle Plugin:
In android/build.gradle
change ext.kotlin_version = '1.3.50'
to ext.kotlin_version = '1.5.10'
In android/build.gradle
change classpath 'com.android.tools.build:gradle:3.5.0'
to classpath 'com.android.tools.build:gradle:4.2.0'
In android/gradle/wrapper/gradle-wrapper.properties
change distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
to distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
In android/app/build.gradle
change defaultConfig{
...
minSdkVersion 16
}
to defaultConfig{
...
minSdkVersion 20
}
If you are using Flutter Beta or Dev channel (1.25 or 1.26) you can get the following error:
java.lang.AbstractMethodError: abstract method "void io.flutter.plugin.platform.PlatformView.onFlutterViewAttached(android.view.View)"
This is a bug in Flutter which is being tracked here: https://github.com/flutter/flutter/issues/72185
There is a workaround by adding android.enableDexingArtifactTransform=false
to your gradle.properties
file.
In order to use this plugin, add the following to your Info.plist file:
<key>io.flutter.embedded_views_preview</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan QR codes</string>
Add this to web/index.html
:
<script src="https://cdn.jsdelivr.net/npm/jsqr@1.3.1/dist/jsQR.min.js"></script>
Please note: on web, only QR codes are supported. Other barcodes and 2D codes cannot be scanned.
Web support is in very early stage. Features such as flash, pause or resume are not implemented. Moreover, the camera preview does not respect the surrounding constraints. This is not at last due to Flutter's early state of platform views on web.
The default camera is the back camera.
await controller.flipCamera();
By default, flash is OFF.
await controller.toggleFlash();
Pause camera stream and scanner.
await controller.pauseCamera();
Resume camera stream and scanner.
await controller.resumeCamera();
SDK
Requires at least SDK 20. Requires at least iOS 8.
TODOs
Credits
Run this command:
With Flutter:
$ flutter pub add qr_code_scanner
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
qr_code_scanner: ^1.0.1
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
void main() => runApp(const MaterialApp(home: MyHome()));
class MyHome extends StatelessWidget {
const MyHome({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Flutter Demo Home Page')),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const QRViewExample(),
));
},
child: const Text('qrView'),
),
),
);
}
}
class QRViewExample extends StatefulWidget {
const QRViewExample({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _QRViewExampleState();
}
class _QRViewExampleState extends State<QRViewExample> {
Barcode? result;
QRViewController? controller;
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
// In order to get hot reload to work we need to pause the camera if the platform
// is android, or resume the camera if the platform is iOS.
@override
void reassemble() {
super.reassemble();
if (Platform.isAndroid) {
controller!.pauseCamera();
}
controller!.resumeCamera();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Expanded(flex: 4, child: _buildQrView(context)),
Expanded(
flex: 1,
child: FittedBox(
fit: BoxFit.contain,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
if (result != null)
Text(
'Barcode Type: ${describeEnum(result!.format)} Data: ${result!.code}')
else
const Text('Scan a code'),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.toggleFlash();
setState(() {});
},
child: FutureBuilder(
future: controller?.getFlashStatus(),
builder: (context, snapshot) {
return Text('Flash: ${snapshot.data}');
},
)),
),
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.flipCamera();
setState(() {});
},
child: FutureBuilder(
future: controller?.getCameraInfo(),
builder: (context, snapshot) {
if (snapshot.data != null) {
return Text(
'Camera facing ${describeEnum(snapshot.data!)}');
} else {
return const Text('loading');
}
},
)),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.pauseCamera();
},
child: const Text('pause',
style: TextStyle(fontSize: 20)),
),
),
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.resumeCamera();
},
child: const Text('resume',
style: TextStyle(fontSize: 20)),
),
)
],
),
],
),
),
)
],
),
);
}
Widget _buildQrView(BuildContext context) {
// For this example we check how width or tall the device is and change the scanArea and overlay accordingly.
var scanArea = (MediaQuery.of(context).size.width < 400 ||
MediaQuery.of(context).size.height < 400)
? 150.0
: 300.0;
// To ensure the Scanner view is properly sizes after rotation
// we need to listen for Flutter SizeChanged notification and update controller
return QRView(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
overlay: QrScannerOverlayShape(
borderColor: Colors.red,
borderRadius: 10,
borderLength: 30,
borderWidth: 10,
cutOutSize: scanArea),
onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p),
);
}
void _onQRViewCreated(QRViewController controller) {
setState(() {
this.controller = controller;
});
controller.scannedDataStream.listen((scanData) {
setState(() {
result = scanData;
});
});
}
void _onPermissionSet(BuildContext context, QRViewController ctrl, bool p) {
log('${DateTime.now().toIso8601String()}_onPermissionSet $p');
if (!p) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('no Permission')),
);
}
}
@override
void dispose() {
controller?.dispose();
super.dispose();
}
}
Download details:
Author: juliuscanute