DEV Community

Xiao Ling
Xiao Ling

Posted on • Originally published at


How to Implement a Flutter QR Code Scanner Plugin for iOS in Swift

Previously, I wrote a blog post sharing how to implement a Flutter QR code scanner plugin for Android. For cross-platform mobile development, a plugin is not perfect if it does not support both Android and iOS. In this article, I am going to complement the iOS part of the plugin in order to bring the best development experience for mobile developers.

Flutter QR Code Scanner Plugin

Dev Environment

  • M1 Mac
  • Xcode 13.2.1

Step-by-step Guide: Implement Flutter QR Code Scanner Plugin for iOS

Step 1: Get the Existing Flutter Plugin Project:

git clone
Enter fullscreen mode Exit fullscreen mode

Step 2: Add Support for iOS

cd flutter_qrcode_scanner
flutter create --template=plugin --platforms=ios .
Enter fullscreen mode Exit fullscreen mode

Step 3: Install Dynamsoft Camera Enhancer and Dynamsoft Barcode Reader

Open iOS/flutter_camera_qrcode_scanner.podspec to add Dynamsoft Camera Enhancer 2.1.1 and Dynamsoft Barcode Reader 8.9.1:

s.dependency 'DynamsoftBarcodeReader', '8.9.1'
s.dependency 'DynamsoftCameraEnhancer', '2.1.1'
Enter fullscreen mode Exit fullscreen mode

The dependencies will be installed via pod install.

Activating Mobile QR Code SDK

Click here to get a valid license key for Dynamsoft Barcode Reader.

Step 4: Implement the Factory and the Platform View Using Swift Code

Since we have completed the code logic on the Dart side, we only need to focus on the platform-related code. According to the official tutorial and the Android part of the plugin, we can write Swift code for iOS as follows:

  1. Based on the structure of the Android QRCodeScanner class, we create a FLQRCodeScanner class in iOS/Classes/FLQRCodeScanner.swift:

    import Flutter
    import UIKit
    import DynamsoftBarcodeReader
    import DynamsoftCameraEnhancer
    public protocol DetectionHandler {
            func onDetected(data: NSArray)
    class FLQRCodeScanner: NSObject, DBRTextResultDelegate {
        private var cameraView: DCECameraView
        private var dce: DynamsoftCameraEnhancer
        private var barcodeReader: DynamsoftBarcodeReader! = nil
        private var handler: DetectionHandler?
        init(cameraView: DCECameraView, dce: DynamsoftCameraEnhancer) {
            self.cameraView = cameraView
            self.cameraView.overlayVisible = true
            self.dce = dce
            createBarcodeReader(dce: dce)
        func setDetectionHandler(handler: DetectionHandler) {
            self.handler = handler;
        func createBarcodeReader(dce: DynamsoftCameraEnhancer) {
            // To activate the sdk, apply for a license key:
            barcodeReader = DynamsoftBarcodeReader.init(license: "license-key")
            // Set text result call back to get barcode results.
            barcodeReader.setDBRTextResultDelegate(self, userData: nil)
            // Start the barcode decoding thread.
        func textResultCallback(_ frameId: Int, results: [iTextResult]?, userData: NSObject?) {
            if results!.count > 0 {
                let outResults = NSMutableArray()
                for item in results! {
                    let subDic = NSMutableDictionary()
                    if item.barcodeFormat_2 != EnumBarcodeFormat2.Null {
                        subDic.setObject(item.barcodeFormatString_2 ?? "", forKey: "format" as NSCopying)
                        subDic.setObject(item.barcodeFormatString ?? "", forKey: "format" as NSCopying)
                    subDic.setObject(item.barcodeText ?? "", forKey: "text" as NSCopying)
                    let points = item.localizationResult?.resultPoints as! [CGPoint]
                    subDic.setObject(Int(points[0].x), forKey: "x1" as NSCopying)
                    subDic.setObject(Int(points[0].y), forKey: "y1" as NSCopying)
                    subDic.setObject(Int(points[1].x), forKey: "x2" as NSCopying)
                    subDic.setObject(Int(points[1].y), forKey: "y2" as NSCopying)
                    subDic.setObject(Int(points[2].x), forKey: "x3" as NSCopying)
                    subDic.setObject(Int(points[2].y), forKey: "y3" as NSCopying)
                    subDic.setObject(Int(points[3].x), forKey: "x4" as NSCopying)
                    subDic.setObject(Int(points[3].y), forKey: "y4" as NSCopying)
                    subDic.setObject(item.localizationResult?.angle ?? 0, forKey: "angle" as NSCopying)
                if handler != nil {
                    handler!.onDetected(data: outResults)
        func startScan() {
            cameraView.overlayVisible = true
        func stopScan() {
            cameraView.overlayVisible = false
        func setBarcodeFormats(arg:NSDictionary) {
            let formats:Int = arg.value(forKey: "formats") as! Int
            let settings = try! barcodeReader!.getRuntimeSettings()
            settings.barcodeFormatIds = formats
            barcodeReader!.update(settings, error: nil)
        func setLicense(license: String) {
            barcodeReader.license = license
  2. Then we create a FLNativeView class in iOS/Classes/FLNativeView.swift to implement the camera view:

    import Flutter
    import UIKit
    import DynamsoftCameraEnhancer
    class FLNativeView: NSObject, FlutterPlatformView, DetectionHandler {
        private var _view: UIView
        private var messenger: FlutterBinaryMessenger
        private var channel: FlutterMethodChannel
        private var qrCodeScanner: FLQRCodeScanner
            frame: CGRect,
            viewIdentifier viewId: Int64,
            arguments args: Any?,
            binaryMessenger: FlutterBinaryMessenger
        ) {
            self.messenger = binaryMessenger
            let cameraView = DCECameraView.init(frame: frame)
            let dce = DynamsoftCameraEnhancer.init(view: cameraView)
            _view = cameraView
            qrCodeScanner = FLQRCodeScanner.init(cameraView: cameraView, dce: dce)
            channel = FlutterMethodChannel(name: "com.dynamsoft.flutter_camera_qrcode_scanner/nativeview_" + String(viewId), binaryMessenger: messenger)
            qrCodeScanner.setDetectionHandler(handler: self)
            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
                switch call.method {
                    case "startScanning":
                    case "stopScanning":
                    case "setLicense":
                        self.qrCodeScanner.setLicense(license: (call.arguments as! NSDictionary).value(forKey: "license") as! String)
                    case "setBarcodeFormats":
                        self.qrCodeScanner.setBarcodeFormats(arg: call.arguments as! NSDictionary)
        func view() -> UIView {
            return _view
        func onDetected(data: NSArray) {
            DispatchQueue.main.async {
          "onDetected", arguments: data)

    The native view contains a channel to handle Flutter method calls (from Dart to native). It also triggers the onDetected callback function (from native to Dart). When a QR code is detected, the channel will send the QR information from the native side to the Dart side .

  3. Next, create a FLNativeViewFactory class in iOS/Classes/FLNativeViewFactory.swift to initialize the native view:

    import Flutter
    import UIKit
    class FLNativeViewFactory: NSObject, FlutterPlatformViewFactory {
        private var messenger: FlutterBinaryMessenger
        init(messenger: FlutterBinaryMessenger) {
            self.messenger = messenger
        func create(
            withFrame frame: CGRect,
            viewIdentifier viewId: Int64,
            arguments args: Any?
        ) -> FlutterPlatformView {
            return FLNativeView(
                frame: frame,
                viewIdentifier: viewId,
                arguments: args,
                binaryMessenger: messenger
  4. The final step is to register the factory in iOS/Classes/SwiftFlutterCameraQrcodeScannerPlugin.swift:

    import Flutter
    import UIKit
    public class SwiftFlutterCameraQrcodeScannerPlugin: NSObject, FlutterPlugin {
      public static func register(with registrar: FlutterPluginRegistrar) {  
        let factory = FLNativeViewFactory(messenger: registrar.messenger())
        registrar.register(factory, withId: "com.dynamsoft.flutter_camera_qrcode_scanner/nativeview")

So far, the native Swift code for plugin is done.

Step 5: Test the QR code scanner in Flutter

  1. Go to the example folder and add the camera access permission to ios/Runner/Info.plist:

    <string>Can I use the camera please?</string>
    <string>Can I use the mic please?</string>
  2. Run the example on iOS devices:

    flutter run

    Flutter iOS QR code scanner

Source Code

Latest comments (0)

An Animated Guide to Node.js Event Loop

Node.js doesnโ€™t stop from running other operations because of Libuv, a C++ library responsible for the event loop and asynchronously handling tasks such as network requests, DNS resolution, file system operations, data encryption, etc.

What happens under the hood when Node.js works on tasks such as database queries? We will explore it by following this piece of code step by step.