Previously, I shared how to support Android and Windows in the fluent plug-in. This article will add the implementation method of Web plug-in and create a simple web one-dimensional code and two-dimensional code recognition application.
Reference resources
- https://dart.dev/web/js-interop
- https://github.com/grandnexus/firebase-dart
- https://pub.dev/packages/js
Developing the fluent web plug-in
The main problem in Web plug-in development is how to realize the mutual call between Dart and JavaScript. Firebase provided on the official website_ Web examples are worth learning and reference.
Initialize web plug-in
Add a Web template to the current plug-in project:
flutter create --template=plugin --platforms=web .
Unlike windows and Android, the web template does not generate a directory called web, nor does it generate any JavaScript code files. We only see a new flitter_ barcode_ sdk_ web. Dart file.
Next, add the plug-in declaration to pubspec In yaml:
flutter: plugin: platforms: android: package: com.dynamsoft.flutter_barcode_sdk pluginClass: FlutterBarcodeSdkPlugin windows: pluginClass: FlutterBarcodeSdkPlugin web: pluginClass: FlutterBarcodeSdkWeb fileName: flutter_barcode_sdk_web.dart
How to realize the interaction between JavaScript and Dart
Like other platforms, handleMethodCall() is the entry:
Future<dynamic> handleMethodCall(MethodCall call) async { switch (call.method) { case 'getPlatformVersion': return getPlatformVersion(); default: throw PlatformException( code: 'Unimplemented', details: 'flutter_barcode_sdk for web doesn\'t implement \'${call.method}\'', ); } }
But the difference is that the web does not need to introduce dependency library compilation into the plug-in. All we have to do is define the interface.
I have defined two interfaces: decodeFile() and decodeVideo(), which are used to recognize one-dimensional code and two-dimensional code through image and video respectively.
BarcodeManager _barcodeManager = BarcodeManager(); /// Decode barcodes from an image file. Future<List<Map<dynamic, dynamic>>> decodeFile(String file) async { return _barcodeManager.decodeFile(file); } /// Decode barcodes from real-time video stream. Future<void> decodeVideo() async { _barcodeManager.decodeVideo(); }
In video mode, the result is returned through callback. However, in the upper layer of the flutter_ barcode_ sdk. In dart, the callback function reference cannot be passed down through invokeMethod. My solution is to use global variables to save callback functions:
Future<void> decodeVideo(Function callback) async { globalCallback = callback; await _channel.invokeMethod('decodeVideo'); }
To avoid conflicts with other platforms, this variable is defined separately in a global In dart file:
Function globalCallback = () => {};
Now open barcode_manager.dart, according to dynamsoft-javascript-barcode Define the calling interface of JavaScript:
@JS('Dynamsoft') library dynamsoft; import 'dart:convert'; import 'dart:js'; import 'package:flutter_barcode_sdk/barcode_result.dart'; import 'package:flutter_barcode_sdk/global.dart'; import 'package:js/js.dart'; import 'utils.dart'; /// BarcodeScanner class @JS('DBR.BarcodeScanner') class BarcodeScanner { external static PromiseJsImpl<BarcodeScanner> createInstance(); external void show(); external set onFrameRead(Function func); } /// BarcodeReader class @JS('DBR.BarcodeReader') class BarcodeReader { external static PromiseJsImpl<BarcodeReader> createInstance(); external PromiseJsImpl<List<dynamic>> decode(dynamic file); }
In order to implement the Promise of JavaScript, we need another utils Defined in dart file:
import 'dart:async'; import 'dart:js_util'; import 'package:js/js.dart'; typedef Func1<A, R> = R Function(A a); @JS('JSON.stringify') external String stringify(Object obj); @JS('console.log') external void log(Object obj); @JS('Promise') class PromiseJsImpl<T> extends ThenableJsImpl<T> { external PromiseJsImpl(Function resolver); external static PromiseJsImpl<List> all(List<PromiseJsImpl> values); external static PromiseJsImpl reject(error); external static PromiseJsImpl resolve(value); } @anonymous @JS() abstract class ThenableJsImpl<T> { external ThenableJsImpl then([Func1 onResolve, Func1 onReject]); } Future<T> handleThenable<T>(ThenableJsImpl<T> thenable) => promiseToFuture(thenable);
Next, implement object initialization:
/// Initialize Barcode Scanner. void initBarcodeScanner(BarcodeScanner scanner) { _barcodeScanner = scanner; _barcodeScanner.onFrameRead = allowInterop((results) => {globalCallback(callbackResults(_resultWrapper(results)))}); } /// Initialize Barcode Reader. void initBarcodeReader(BarcodeReader reader) { _barcodeReader = reader; } BarcodeManager() { handleThenable(BarcodeScanner.createInstance()) .then((scanner) => {initBarcodeScanner(scanner)});
Dart's function needs to be encapsulated by allowInterop() before it can be called by JavaScript.
Implement decodeFile():
Future<List<Map<dynamic, dynamic>>> decodeFile(String filename) async { List<dynamic> barcodeResults = await handleThenable(_barcodeReader.decode(filename)); return _resultWrapper(barcodeResults); }
Implement the decodeVideo() callback:
_barcodeScanner.onFrameRead = allowInterop((results) => {globalCallback(callbackResults(_resultWrapper(results)))});
Create Web one-dimensional code and two-dimensional code recognition program
Create a new fluent project and create it on the web / index Add < script SRC to HTML=“ https://cdn.jsdelivr.net/npm/dynamsoft-javascript-barcode @8.2.3/dist/dbr. js" data-productKeys="PRODUCT-KEYS"></script>.
In pubspec Add image to yaml_ Picker and flitter_ barcode_ sdk:
dependencies: flutter_barcode_sdk: image_picker:
Add two buttons in the UI, one for loading pictures and one for starting camera video streaming:
final picker = ImagePicker(); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text('Dynamsoft Barcode Reader'), ), body: Column(children: [ Container( height: 100, child: Row(children: <Widget>[ Text( _platformVersion, style: TextStyle(fontSize: 14, color: Colors.black), ) ]), ), Expanded( child: SingleChildScrollView( child: Column( children: [ _file == null ? Image.asset('images/default.png') : Image.network(_file), Text( _barcodeResults, style: TextStyle(fontSize: 14, color: Colors.black), ), ], ), ), ), Container( height: 100, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ MaterialButton( child: Text('Barcode Reader'), textColor: Colors.white, color: Colors.blue, onPressed: () async { final pickedFile = await picker.getImage(source: ImageSource.camera); setState(() { if (pickedFile != null) { _file = pickedFile.path; } else { print('No image selected.'); } _barcodeResults = ''; }); if (_file != null) { List<BarcodeResult> results = await _barcodeReader.decodeFile(_file); updateResults(results); } }), MaterialButton( child: Text('Barcode Scanner'), textColor: Colors.white, color: Colors.blue, onPressed: () async { _barcodeReader.decodeVideo( (results) => {updateResults(results)}); }), ]), ), ])), );
Finally run the program:
flutter run -d chrome
Fluent plug-in download
https://pub.dev/packages/flutter_barcode_sdk