How to Create A Simple Flutter Barcode Plugin from Scratch

I think Flutter is vital for developers. Not only it supports fast building Android and iOS apps from a single codebase, but also it is used for building the upcoming Google Fuchsia apps. Like React Native and Cordova, the Flutter ecosystem consists of amounts of plugins, which empower Flutter apps, especially for platform-specific functionalities. In this article, I will share how to create a simple Flutter barcode plugin with Dynamsoft Barcode Reader SDK from scratch.

Developing Flutter Barcode Plugin Package

Create the plugin package skeleton:

flutter create --org com.dynamsoft --template=plugin barcode_reader

By default, the command above generates Java code for Android and Objective-C code for iOS. You can specify the programming language using -i (iOS) and -a (Android). For example:

flutter create --org com.dynamsoft --template=plugin -i swift -a kotlin barcode_reader

Although Swift and Kotlin are now officially preferred programming languages for iOS and Android, I suggest using Objective-C and Java to avoid building issues.

In addition to the platform-specific code, the project also contains an example. Try the example:

cd barcode_reader/example
flutter run

If you have more than one devices connected to USB port, select a target device:

flutter devices
flutter run -d <Device ID>

Add methods initLicense() and decodeFile() to barcode_reader/lib/barcode_reader.dart:

  Future<void> initLicense(String license) async {
    await _channel.invokeMethod('initLicense', [license]);
  }

  Future<String> decodeFile(String filename) async {
    return await _channel.invokeMethod('decodeFile', [filename]);
  }

Android platform code

Add the dependency in barcode_reader/android/build.gradle:

rootProject.allprojects {
    repositories {
        google()
        jcenter()
        maven {
            url "http://download.dynamsoft.com/maven/dbr/aar"
        }
    }
}
dependencies {
    implementation 'com.dynamsoft:dynamsoftbarcodereader:7.0.0@aar'
}

Add Java code to implement the method call in barcode_reader/android/src/main/java/com/Dynamsoft/barcode_reader/ BarcodeReaderPlugin.java:

@Override
  public void onMethodCall(MethodCall call, Result result) {
    ArrayList<String> args = (ArrayList<String>)call.arguments;
    if (call.method.equals("getPlatformVersion")) {
      result.success("Android " + android.os.Build.VERSION.RELEASE);
    } else if (call.method.equals("initLicense")) {
      mBarcodeManager.initLicense(args.get(0));
    } else if (call.method.equals("decodeFile")) {
      String results = mBarcodeManager.decodeFile(args.get(0));
      result.success(results);
    } else {
      result.notImplemented();
    }
  }

public class BarcodeManager {
    private static final String TAG = "DynamsoftBarcodeReader";
    private BarcodeReader mBarcodeReader;

    public BarcodeManager() {
        try {
            mBarcodeReader = new BarcodeReader();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void initLicense(String license) {
        try {
            Log.i(TAG, license);
            mBarcodeReader.initLicense(license);
        }
        catch (BarcodeReaderException e) {
            e.printStackTrace();
        }
    }

    public String decodeFile(String filename) {
        try {
            TextResult[] results = mBarcodeReader.decodeFile(filename, "");
            if (results != null) {
                String ret = "";
                for (TextResult result: results) {
                    ret += "Format: " + result.barcodeFormatString + ", ";
                    ret += "Text: " + result.barcodeText + ". ";
                }

                return ret;
            }
        } catch (BarcodeReaderException e) {
            e.printStackTrace();
        }
        return "No Barcode Detected.";
    }
}

iOS platform code

Download Dynamsoft Barcode Reader for iOS.

Extract the SDK package and copy DynamsoftBarcodeReader.framework to barcode_reader/ios folder.

Configure the dependency in barcode_reader.podspec:

s.libraries             = 'stdc++'
s.vendored_frameworks = 'DynamsoftBarcodeReader.framework'

Add Objective-C code to implement the method call in barcode_reader/ios/Classes/ BarcodeReaderPlugin.m:

#import "BarcodeReaderPlugin.h"
#import <DynamsoftBarcodeReader/DynamsoftBarcodeSDK.h>

@implementation BarcodeReaderPlugin
{
  DynamsoftBarcodeReader *barcodeReader;
}

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
  FlutterMethodChannel* channel = [FlutterMethodChannel
      methodChannelWithName:@"barcode_reader"
            binaryMessenger:[registrar messenger]];
  BarcodeReaderPlugin* instance = [[BarcodeReaderPlugin alloc] init];
  [registrar addMethodCallDelegate:instance channel:channel];
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
  NSString *value = call.arguments[0];
  if ([@"getPlatformVersion" isEqualToString:call.method]) {
    result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
  }
  else if ([@"initLicense" isEqualToString:call.method]) {
    barcodeReader = [[DynamsoftBarcodeReader alloc] initWithLicense:value];
  } 
  else if ([@"decodeFile" isEqualToString:call.method]) {
    if (barcodeReader != nil) {
      NSArray* results = [barcodeReader decodeFileWithName:value templateName:@"" error:nil];
      if(results == nil) {
        result(@"No Barcode Detected.");
      }
      else {
        iTextResult* barcode = (iTextResult*)results.firstObject;
        if (barcode.barcodeText == nil) {
          result(@"No Barcode Detected.");
        }
        else {
          NSString* msgText = [NSString stringWithFormat:@"Format: %@, Text: %@.", barcode.barcodeFormatString, barcode.barcodeText];
          result(msgText);
        }
      }
    }
    else {
      result(@"Failed to create barcode reader!");
    }
  }
  else {
    result(FlutterMethodNotImplemented);
  }
}

@end

Barcode Reader App

Create a Flutter project:

flutter create demo

Android

Change the minSdkVersion from 16 (the default SDK version) to 21 in demo/android/app/build.gralde:

defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.dynamsoft.demo"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

iOS

Add two usage descriptions to demo/ios/Runner/Info.plist:

<key>NSCameraUsageDescription</key>
<string>Can I use the camera please?</string>
<key>NSMicrophoneUsageDescription</key>
<string>Can I use the mic please?</string>

Follow the tutorial – Take a picture using the camera – to create a camera app by modifying demo/lib/main.dart.

Get a free 30-day trial license of Dynamsoft Barcode Reader:

Set the license:

@override
  void initState() {
    super.initState();
    // To display the current output from the Camera,
    // create a CameraController.
    _controller = CameraController(
      // Get a specific camera from the list of available cameras.
      widget.camera,
      // Define the resolution to use.
      ResolutionPreset.medium,
    );

    // Next, initialize the controller. This returns a Future.
    _initializeControllerFuture = _controller.initialize();

    // Initialize Dynamsoft Barcode Reader
    initDynamsoftBarcodeReaderState();
  }

  Future<void> initDynamsoftBarcodeReaderState() async {
    _barcodeReader = BarcodeReader();
    await _barcodeReader.initLicense('LICENSE-KEY');
  }

When the temporary image is available after taking the picture, call the decodeFile() function:

await _controller.takePicture(path);
String results = await _barcodeReader.decodeFile(path);

To show result text over the captured image, use Stack layout:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Dynamsoft Barcode Reader')),
      // The image is stored as a file on the device. Use the `Image.file`
      // constructor with the given path to display the image.
      body: Stack(
        alignment: const Alignment(0.0, 0.0),
        children: [
          // Show full screen image: https://stackoverflow.com/questions/48716067/show-fullscreen-image-at-flutter
          Image.file(
            File(imagePath),
            fit: BoxFit.cover,
            height: double.infinity,
            width: double.infinity,
            alignment: Alignment.center,
          ),
          Container(
            decoration: BoxDecoration(
              color: Colors.black45,
            ),
            child: Text(
              barcodeResults,
              style: TextStyle(
                fontSize: 20,
                fontWeight: FontWeight.bold,
                color: Colors.white,
              ),
            ),
          ),
        ],
      ),
    );
  }

Run the app for iOS:

cd demo/ios
pod install
cd ..
flutter run -d <iOS Device ID>

Run the app for Android:

cd demo
flutter run -d <Android Device ID>

Barcode Reader App

flutter barcode plugin

Reference

  • https://flutter.dev/docs/development/packages-and-plugins/developing-packages
  • https://flutter.dev/docs/cookbook/plugins/picture-using-camera
  • https://pub.dev/packages/camera

Source Code

https://github.com/yushulx/flutter-barcode-plugin