Implementation of fluent web plug-in: get through JavaScript and Dart

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

Source code

https://github.com/yushulx/flutter_barcode_sdk

Keywords: Javascript Web Development Flutter

Added by ksduded on Thu, 10 Feb 2022 14:05:00 +0200