How To Write a Simple Flutter QR Code Scanner App
14 April 2021
Intro
In this article you will learn how to write a simple iOS and Android app which:
- scans QR codes witch a phone camera,
- if scanned data is a link it opens it in a browser,
- otherwise it displays it in a dialog.
If you are new to Flutter and you have never built nor created an application using Flutter, please get familiar with Get started section on the official flutter.dev website before continuing.
Step 0. Setting up the project
First of all, generate a new Flutter project if you haven’t done it already 🙂
Step 1. Adding dependencies
Add following dependencies to the pubspec.yaml file:
qr_code_scanner: ^0.4.0
Url_launcher: ^6.0.3
qr_code_scanner — A QR code scanner that works on both iOS and Android by natively embedding the platform view within Flutter. Note that this flutter plugin also has a barcode scanning support.
url_launcher — A Flutter plugin for launching a URL.
Step 2. Setting up minimal required versions
Qr_scanner requires minimal versions to be set for both Android and iOS platform.
- iOS
Go to ios/Flutter/AppFrameworkInfo.plist and find
<key>MinimumOSVersion</key>
Set its value to 8.0 in a following line:
<string>8.0</string>
- Android
Go to android/app/build.gradle and find
minSdkVersion
Set sdk value to 21.
Apart from that we don’t need to write any platform specyfic code.
It is the beauty of Flutter apps 😉
Step 3. Starting point
It would be a little weird if our application started with a scanner screen right off the bat so instead we will start by making a button that will take us to the scanner page.
Replace current _MyHomePageState build method with:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: ElevatedButton( child: Text('Scan'), onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => Scanner()), ); }), ), ); }
This won’t work just like that as we did not create Scanner screen, so let’s do it now!
Create scanner.dart file in your lib folder.
Now copy this code into it:
import 'package:flutter/material.dart'; class Scanner extends StatefulWidget { @override _ScannerState createState() => _ScannerState(); } class _ScannerState extends State<Scanner> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Scanner"), ), ); } }
We now have an app with one button that leads to another screen in which we will place our scanning widgets and code.
Step 4. Qr code scanner
Now finally, it is the right time to add scanning functionality to our app!
Add this code inside _ScannerState before build method:
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); QRViewController controller; @override void dispose() { controller?.dispose(); super.dispose(); }
And then inside build method add body to our Scaffold:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Scanner"), ), body: QRView( key: qrKey, onQRViewCreated: _onQRViewCreated, ), ); }
After that add a new method under the build:
void _onQRViewCreated(QRViewController controller) { this.controller = controller; controller.scannedDataStream.listen((scanData) async { //do something }); }
You will also need to import any missing package.
Note that in Flutter if you start a method or field name with underscore it will be marked as a private method.
Step 5. Open up a scanned url link
Copy this code in place of //do something:
controller.pauseCamera(); if (await canLaunch(scanData.code)) { await launch(scanData.code); } controller.resumeCamera();
Note that because _onQRViewCreated function listens to a stream it will fire multiple times before a url link can be checked or launched. This could lead to launching multiple instances of the same page. To prevent that we pause and resume camera work when we check for validity of found data.
Let’s check if it works!
Try scanning QR code on this image:
Congratulations!
At this point we have made QR code scanner app that support iOS and Android. It will scan a QR code and if it contains a link it will be launched in an outside browser. If you want you can stop at this point.
Step 6. Make it Better!
Since our app is only looking for valid url in a QR code it ignores any QR code that doesn’t contain it even if the user is pointing right at it. This is bad as it creates confusion and appears as if the app is not working, so lets display some info dialog if that’s the case.
Modify _onQRViewCreated function:
void _onQRViewCreated(QRViewController controller) { this.controller = controller; controller.scannedDataStream.listen((scanData) async { controller.pauseCamera(); if (await canLaunch(scanData.code)) { await launch(scanData.code); controller.resumeCamera(); } else { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text('Could not find viable url'), content: SingleChildScrollView( child: ListBody( children: <Widget>[ Text('Barcode Type: ${describeEnum(scanData.format)}'), Text('Data: ${scanData.code}'), ], ), ), actions: <Widget>[ TextButton( child: Text('Ok'), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ).then((value) => controller.resumeCamera()); } }); }
Note that we resume camera work only after the url is launched or the user closes the dialog.
This is optional but we can also make the scanner look a bit better. We will add a simple message under camera view and rectangular aim into the said camera. Replace Scaffold body:
body: Stack( children: [ Column( children: <Widget>[ Expanded( flex: 5, child: Stack( children: [ QRView( key: qrKey, onQRViewCreated: _onQRViewCreated, ), Center( child: Container( width: 300, height: 300, decoration: BoxDecoration( border: Border.all( color: Colors.red, width: 4, ), borderRadius: BorderRadius.circular(12), ), ), ) ], ), ), Expanded( flex: 1, child: Center( child: Text('Scan a code'), ), ) ], ), ], ),
Now it should look like this:
Try to scan QR code on this image:
Congratulations!
You now have a Flutter app that can navigate between two screens, scan QR codes and depending on scanned data opens a link in an external browser or displays it to the user. Also note that, while it is not the focus of this tutorial, using this app as barcode scanner is also posible without any changes to the code!
You can find the full example in my GitHub repository.