NoobsPlanet

Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics, posts and unlimited download to our resources, as well as connect with other members through your own private inbox!

Flutter create and scan QRCode example with security. [only your app can read]

noobsplanet

Well-known member
Staff member
Administrator
Moderator
A QR code is an image with basic information in which information is scanned by a QR code reader (or phone camera). QR means Quick Reader, so remember this to implement in a real-world application to identify the object or things. For example, bus tickets, movie tickets, gate pass or e-commerce warehouses to identify the ordered product. So I hope you get the use case of this in a real application. A similar scenario we will be going to use in our example today, that is getting the information of the book from the QR code. Yes let's break down the task:
  • Use simple dio http client for network calls.
  • Create a QR code based on the book id.
  • Encrypt the QR code with Aztec
  • Share the QR code with friends
  • Scan the QR code.
  • Decrypt the QR code.
  • Use flavor to build different product flavors.
  • Test in Android & iOS
The QR code encryption and decryption with Aztec are written in a separate article in the security section. please check the following link if you are only intended to see the security.

First, add the following dependency to pubspec.yaml and run flutter pub get

pubspec.yaml:
environment:
  sdk: '>=2.10.5 <3.0.0'
dependencies:
  barcode: 1.17.1
  barcode_image: 1.2.0
  barcode_widget: 1.5.0
  qr_code_scanner: 0.3.5


dev_dependencies:
  build_runner: 1.11.1
  json_serializable: 3.5.1
Now, we are going to first create the QRCode, by default the BarCode package only accepts the String type to generate the QrCode but what if we need multiple values with a key? Why not object? Yes, in that scenario, we first create an entity and then convert that entity into String after that we encode with Aztec encryption for security so that other scanners cannot read data.

QrObject.dart:
import 'dart:core';

import 'package:auto_route/auto_route.dart';
import 'package:json_annotation/json_annotation.dart';

part 'qr_object.g.dart';

@JsonSerializable()
class QrObject {
  String id;
  String qrType;

  QrObject({@required this.id, @required this.qrType});

  factory QrObject.fromJson(Map<String, dynamic> json) =>
      _$QrObjectFromJson(json);

  Map<String, dynamic> toJson() => _$QrObjectToJson(this);
}
Once, we create a QrObject.dart class, we need to run the following command in the project root directory so that the build_runner could generate the qr_object.dart class.

Terminal:
flutter pub run build_runner build --delete-conflicting-outputs
Once, above build runner is successful, use a BarcodeWidget widget to create a barcode


qr_encryption.dart:
import 'package:encrypt/encrypt.dart';
import 'package:flutter/foundation.dart' as f;

class QrEncryption {
  static final Key key = Key.fromUtf8('F2N3p6RJeC8VCYr6Lg8Dnz2xFkwcrAAG');
  static final Encrypter encrypter = Encrypter(AES(key));
  static final IV iv = IV.fromUtf8('3bBgw3nf9R2KKJFB');

  static String encryptString(String originalText) {
    return encrypter.encrypt(originalText, iv: iv).base64;
  }

  static String decryptString(String encryptedText) {
    try {
      final text =
          encrypter.decrypt(Encrypted.fromBase64(encryptedText), iv: iv);
      f.debugPrint('decrypted Text: $text');
      return text;
    } catch (e) {
      rethrow;
    }
  }
}

qr_code_widget.dart:
BarcodeWidget(
  barcode: Barcode.aztec(),
  data: QrEncryption.encryptString(jsonEncode())),
  height: MediaQuery.of(context).size.height / 2,
)
That's it, you have successfully created the QR code with encryption. The QrCode applications are movies ticket, gate pass, bus ticket, user identity, etc. so which might need to share with friend & family for identity or for backup if you got out of battery. For that purpose the app may have the functionality to share the qrCode right, so the following is the function I've created, just call this function to share the image file.

Dart:
Future<void> _captureAndSharePng() async {
    final qrImage = image.Image(300, 300);
    image.fill(qrImage, image.getColor(255, 255, 255));
    drawBarcode(qrImage, Barcode.aztec(), jsonEncode(qrObject));

    try {
      final tempDir = await getTemporaryDirectory();
      await File('${tempDir.path}/gatepass.png')
          .writeAsBytesSync(image.encodePng(qrImage));
      await Share.shareFiles(['${tempDir.path}/gatepass.png'],
          text: actionBarTitle);
    } catch (e) {
      await EasyLoading.showError('Cannot share the QR.');
    }
  }
}
Now the last thing is the scanner which is used to identity the pass of the user.
qr_code_scanner.dart:
import 'dart:convert';
import 'dart:io';

import 'package:auto_route/auto_route.dart';
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:gatekeeper/core/routes/app_router.gr.dart';
import 'package:gatekeeper/modules/gatepass/domain/entities/qr_object.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';

class QrCodeScannerPage extends StatefulWidget {
  QrCodeScannerPage({Key key}) : super(key: key);

  @override
  _QrCodeScannerPageState createState() => _QrCodeScannerPageState();
}

class _QrCodeScannerPageState extends State<QrCodeScannerPage> {
  final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');

  // Barcode result;
  QRViewController controller;

  // QrObject qrObject;

  @override
  void dispose() {
    controller?.dispose();
    super.dispose();
  }

  @override
  void reassemble() {
    super.reassemble();
    if (Platform.isAndroid) {
      controller.pauseCamera();
    } else if (Platform.isIOS) {
      controller.resumeCamera();
    }
  }

  @override
  Widget build(BuildContext context) {
    final scanArea =
        (ScreenUtil().screenWidth < 400 || ScreenUtil().screenHeight < 400)
            ? 150.0
            : 300.0;
    return Scaffold(
        body: QRView(
      key: qrKey,
      onQRViewCreated: _onQRViewCreated,
      overlay: QrScannerOverlayShape(
        borderColor: colorPrimary,
        borderRadius: 10,
        borderLength: 30,
        borderWidth: 10,
        cutOutSize: scanArea,
      ),
    ));
  }

  void _onQRViewCreated(QRViewController controller) {
    this.controller = controller;
    controller.scannedDataStream.listen((Barcode scanData) {
      controller.stopCamera();
      try {
        final object = QrEncryption.decryptString(scanData.code);
        debugPrint('decrypt success : ${object}');

      //Do whatever you what when in  success.
      } catch (exception) {
        debugPrint('Cannot process the scanned QR');
        controller.resumeCamera();
      }
    });
  }
}
That's it, the code might get some issue due to mismatching names, I will soon try out this example in IDE as an example project and upload to GitHub and let you know here in this forum. Keep this forum alive. 🗣
 
Last edited:
Top