How to Implement a Flutter QR Code Scanner Plugin from Scratch

Installation

allprojects {
repositories {
google()
jcenter()
maven {
url "https://download2.dynamsoft.com/maven/dbr/aar"
}
}
}

dependencies {
implementation 'com.dynamsoft:dynamsoftbarcodereader:8.8.0@aar'
}
allprojects {
repositories {
google()
jcenter()
maven {
url "https://download.dynamsoft.com/maven/dce/aar"
}
}
}
dependencies {
implementation 'com.dynamsoft:dynamsoftcameraenhancer:2.0.0@aar'
}

Creating A Simple Flutter Widget from Android TextView

flutter create --org com.dynamsoft --template=plugin --platforms=android -a java flutter_qrcode_scanner
import 'dart:async';

import 'package:flutter/services.dart';

class FlutterQrcodeScanner {
static const MethodChannel _channel = MethodChannel('flutter_qrcode_scanner');

static Future<String?> get platformVersion async {
final String? version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
}
class ScannerView extends StatefulWidget {
const ScannerView({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _ScannerViewState();
}

class _ScannerViewState extends State<ScannerView> {
@override
void initState() {
super.initState();
}

@override
Widget build(BuildContext context) {
const String viewType = 'com.dynamsoft.flutter_qrcode_scanner/nativeview';
final Map<String, dynamic> creationParams = <String, dynamic>{};

return AndroidView(
viewType: viewType,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
}
import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.plugin.platform.PlatformView;
import java.util.Map;

class NativeView implements PlatformView {
@NonNull private final TextView textView;

NativeView(@NonNull Context context, int id, @Nullable Map<String, Object> creationParams) {
textView = new TextView(context);
textView.setTextSize(72);
textView.setBackgroundColor(Color.rgb(255, 255, 255));
textView.setText("Rendered on a native Android view (id: " + id + ")");
}

@NonNull
@Override
public View getView() {
return textView;
}

@Override
public void dispose() {}
}
import android.content.Context;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;
import java.util.Map;

class NativeViewFactory extends PlatformViewFactory {
@NonNull private final BinaryMessenger messenger;

NativeViewFactory(@NonNull BinaryMessenger messenger) {
super(StandardMessageCodec.INSTANCE);
this.messenger = messenger;
}

@NonNull
@Override
public PlatformView create(@NonNull Context context, int id, @Nullable Object args) {
final Map<String, Object> creationParams = (Map<String, Object>) args;
return new NativeView(context, id, creationParams);
}
}
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_qrcode_scanner");
channel.setMethodCallHandler(this);
flutterPluginBinding.getPlatformViewRegistry().registerViewFactory(
"com.dynamsoft.flutter_qrcode_scanner/nativeview",
new NativeViewFactory(flutterPluginBinding.getBinaryMessenger()));
}
import 'package:flutter/material.dart';
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter_qrcode_scanner/flutter_qrcode_scanner.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);

@override
State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';

@override
void initState() {
super.initState();
}

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: ScannerView(),
),
),
);
}
}
flutter run

Implementing a Flutter Camera Widget from Android Camera View

rootProject.allprojects {
repositories {
google()
mavenCentral()
maven {
url "https://download.dynamsoft.com/maven/dce/aar"
}
}
}

android {
compileSdkVersion 30

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
import com.dynamsoft.dce.*;
private final DCECameraView cameraView;
private CameraEnhancer cameraEnhancer;

NativeView(BinaryMessenger messenger, @NonNull Activity context, int id,
@Nullable Map<String, Object> creationParams) {
this.context = context;

cameraEnhancer = new CameraEnhancer(context);
cameraView = new DCECameraView(context);
cameraEnhancer.setCameraView(cameraView);
try {
cameraEnhancer.open();
} catch (Exception e) {
e.printStackTrace();
}
}
class NativeView implements PlatformView, Application.ActivityLifecycleCallbacks {
@Override
public void onActivityResumed(Activity activity) {
try {
cameraEnhancer.open();
} catch (Exception e) {
e.printStackTrace();
}
}

@Override
public void onActivityPaused(Activity activity) {
try {
cameraEnhancer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class NativeViewFactory extends PlatformViewFactory {
@NonNull private final BinaryMessenger messenger;
@NonNull private Activity activity;

NativeViewFactory(@NonNull BinaryMessenger messenger, Activity activity) {
super(StandardMessageCodec.INSTANCE);
this.messenger = messenger;
this.activity = activity;
}

@NonNull
@Override
public PlatformView create(@NonNull Context context, int id, @Nullable Object args) {
final Map<String, Object> creationParams = (Map<String, Object>) args;
return new NativeView(messenger, activity, id, creationParams);
}
}
public class FlutterQrcodeScannerPlugin implements FlutterPlugin, ActivityAware {
private Activity activity;
private FlutterPluginBinding flutterPluginBinding;

@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
this.flutterPluginBinding = flutterPluginBinding;
}

@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
this.flutterPluginBinding = null;
}

@Override
public void onAttachedToActivity(ActivityPluginBinding activityPluginBinding) {
bind(activityPluginBinding);
}

@Override
public void onReattachedToActivityForConfigChanges(ActivityPluginBinding activityPluginBinding) {
bind(activityPluginBinding);
}

@Override
public void onDetachedFromActivityForConfigChanges() {
activity = null;
}

@Override
public void onDetachedFromActivity() {
activity = null;
}

private void bind(ActivityPluginBinding activityPluginBinding) {
activity = activityPluginBinding.getActivity();
flutterPluginBinding.getPlatformViewRegistry().registerViewFactory(
"com.dynamsoft.flutter_qrcode_scanner/nativeview",
new NativeViewFactory(flutterPluginBinding.getBinaryMessenger(), activity));
}
}

Turning Flutter Camera Plugin into Flutter QR Code Scanner

rootProject.allprojects {
repositories {
google()
mavenCentral()
maven {
url "https://download.dynamsoft.com/maven/dce/aar"
}
maven {
url "https://download.dynamsoft.com/maven/dbr/aar"
}
}
}

android {
compileSdkVersion 30

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

defaultConfig {
minSdkVersion 21
}
}

dependencies {
implementation 'com.dynamsoft:dynamsoftcameraenhancer:2.0.0@aar'
implementation 'com.dynamsoft:dynamsoftbarcodereader:8.8.0@aar'
}
public class QRCodeScanner {
private CameraEnhancer mCameraEnhancer;
private BarcodeReader reader;
private Activity context;
private DCECameraView cameraView;
private DetectionHandler handler;

public interface DetectionHandler {
public void onDetected(List<Map<String, Object>> data);
}

public QRCodeScanner(Activity context, DCECameraView cameraView) {
this.context = context;
this.cameraView = cameraView;
mCameraEnhancer = new CameraEnhancer(context);
mCameraEnhancer.setCameraView(cameraView);
cameraView.setOverlayVisible(true);

DCESettingParameters dceSettingParameters = new DCESettingParameters();
dceSettingParameters.cameraInstance = mCameraEnhancer;
dceSettingParameters.textResultCallback = mTextResultCallback;

try {
// mCameraEnhancer.open();
reader = new BarcodeReader();
reader.SetCameraEnhancerParam(dceSettingParameters);
PublicRuntimeSettings settings = reader.getRuntimeSettings();
settings.barcodeFormatIds = EnumBarcodeFormat.BF_QR_CODE;
reader.updateRuntimeSettings(settings);
} catch (Exception e) {
// TODO: handle exception
}
}

TextResultCallback mTextResultCallback = new TextResultCallback() {
@Override
public void textResultCallback(int i, TextResult[] results, Object userData) {
if (results != null) {
final List<Map<String, Object>> ret = new ArrayList<Map<String, Object>>();
for (TextResult result: results) {
final Map<String, Object> data = new HashMap<>();
data.put("format", result.barcodeFormatString);
data.put("text", result.barcodeText);
data.put("x1", result.localizationResult.resultPoints[0].x);
data.put("y1", result.localizationResult.resultPoints[0].y);
data.put("x2", result.localizationResult.resultPoints[1].x);
data.put("y2", result.localizationResult.resultPoints[1].y);
data.put("x3", result.localizationResult.resultPoints[2].x);
data.put("y3", result.localizationResult.resultPoints[2].y);
data.put("x4", result.localizationResult.resultPoints[3].x);
data.put("y4", result.localizationResult.resultPoints[3].y);
data.put("angle", result.localizationResult.angle);
ret.add(data);
}

if (handler != null) {
handler.onDetected(ret);
}
}
}
};

public void setDetectionHandler(DetectionHandler handler) {
this.handler = handler;
}

public void startScan() {
try {
// mCameraEnhancer.open();
cameraView.setOverlayVisible(true);
reader.StartCameraEnhancer();
} catch (Exception e) {
// TODO: handle exception
}
}

public void stopScan() {
try {
// mCameraEnhancer.close();
cameraView.setOverlayVisible(false);
reader.StopCameraEnhancer();
} catch (Exception e) {
// TODO: handle exception
}
}

public void setLicense(String license) {
try {
reader.initLicense(license);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class NativeView implements PlatformView, MethodCallHandler, Application.ActivityLifecycleCallbacks, QRCodeScanner.DetectionHandler {
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
switch (call.method) {
case "startScanning":
qrCodeScanner.startScan();
result.success(null);
break;
case "stopScanning":
qrCodeScanner.stopScan();
result.success(null);
break;
case "setLicense":
final String license = call.argument("license");
qrCodeScanner.setLicense(license);
result.success(null);
break;
case "setBarcodeFormats":
final int formats = call.argument("formats");
qrCodeScanner.setBarcodeFormats(formats);
result.success(null);
break;
default:
result.notImplemented();
}
}
}
@Overridepublic void onDetected(List<Map<String, Object>> data) {
context.runOnUiThread(new Runnable() {
@Override
public void run() {
methodChannel.invokeMethod("onDetected", data);
}
});
}
class _ScannerViewState extends State<ScannerView> {
ScannerViewController? _controller;

@override
void initState() {
super.initState();
}

@override
Widget build(BuildContext context) {
const String viewType = 'com.dynamsoft.flutter_qrcode_scanner/nativeview';
final Map<String, dynamic> creationParams = <String, dynamic>{};

return AndroidView(
viewType: viewType,
onPlatformViewCreated: _onPlatformViewCreated,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}

void _onPlatformViewCreated(int id) {
_controller = ScannerViewController(id);
widget.onScannerViewCreated(_controller!);
}
}

class ScannerViewController {
late MethodChannel _channel;
final StreamController<List<BarcodeResult>> _streamController =
StreamController<List<BarcodeResult>>();
Stream<List<BarcodeResult>> get scannedDataStream => _streamController.stream;

ScannerViewController(int id) {
_channel =
MethodChannel('com.dynamsoft.flutter_qrcode_scanner/nativeview_$id');
_channel.setMethodCallHandler((call) async {
switch (call.method) {
case 'onDetected':
if (call.arguments != null) {
List<BarcodeResult> data = fromPlatformData(call.arguments as List);
_streamController.sink.add(data);
}
break;
}
});
}
}
Future<void> startScanning() async {
await _channel.invokeMethod('startScanning');
}

Future<void> stopScanning() async {
await _channel.invokeMethod('stopScanning');
}

/// Apply for a 30-day FREE trial license: https://www.dynamsoft.com/customer/license/trialLicense
Future<void> setLicense(String license) async {
await _channel.invokeMethod('setLicense', {'license': license});
}
import 'package:flutter/material.dart';
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter_camera_qrcode_scanner/flutter_qrcode_scanner.dart';
import 'package:flutter_camera_qrcode_scanner/dynamsoft_barcode.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);

@override
State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
ScannerViewController? controller;
String _barcodeResults = '';

@override
void initState() {
super.initState();
}

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('QR Code Scanner'),
),
body: Stack(children: <Widget>[
ScannerView(onScannerViewCreated: onScannerViewCreated),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
height: 100,
child: SingleChildScrollView(
child: Text(
_barcodeResults,
style: TextStyle(fontSize: 14, color: Colors.white),
),
),
),
Container(
height: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
MaterialButton(
child: Text('Start Scan'),
textColor: Colors.white,
color: Colors.blue,
onPressed: () async {
controller!.startScanning();
}),
MaterialButton(
child: Text("Stop Scan"),
textColor: Colors.white,
color: Colors.blue,
onPressed: () async {
controller!.stopScanning();
})
]),
),
],
)
]),
),
);
}

void onScannerViewCreated(ScannerViewController controller) {
setState(() {
this.controller = controller;
});
controller.setLicense('LICENSE-KEY');
controller.startScanning(); // auto start scanning
controller.scannedDataStream.listen((results) {
setState(() {
_barcodeResults = getBarcodeResults(results);
});
});
}

String getBarcodeResults(List<BarcodeResult> results) {
StringBuffer sb = new StringBuffer();
for (BarcodeResult result in results) {
sb.write(result.format);
sb.write("\n");
sb.write(result.text);
sb.write("\n\n");
}
if (results.isEmpty) sb.write("No QR Code Detected");
return sb.toString();
}

Download Flutter QR Code Scanner from pub.dev

Source Code

--

--

--

Manager of Dynamsoft Open Source Projects | Tech Lover

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

How to change Menu SearchView Widget Icon Color In Android

Android SearchView in ActionBar— Androidx and Kotlin

Android: Hide API keys in native libraries using NDK

Fighting Regressions with Benchmarks in CI

Porting an HTML/PixiJS game to Android

Object Detection in Android Using Firebase ML Kit

Improve RecyclerView Performance

1.000 version of Fenomy mobile app for Android!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Xiao Ling

Xiao Ling

Manager of Dynamsoft Open Source Projects | Tech Lover

More from Medium

How to debug code dependencies on VS code

Searching a bug

Building a Flutter Computer Vision App Using Dart:ffi, OpenCV, and Tensorflow (Part 1)

Sudoku Cam, A Flutter App

Using Riverpod Future Provider to Fetch API

Use MQTT in Flutter — Flutter IoT