每日一博 | Flutter Web 插件实现:打通 JavaScript 和 Dart
之前分享了如何在Flutter插件中支持Android和Windows,这篇文章将增加Web插件的实现方法,以及创建一个简单的web一维码,二维码识别应用。
参考资源
- https://dart.dev/web/js-interop
- https://github.com/grandnexus/firebase-dart
- https://pub.dev/packages/js
开发Flutter Web插件
Web插件开发,主要问题是如何实现Dart和JavaScript的相互调用。官网提供的firebase_web示例值得学习和参考。
初始化web插件
在当前的插件工程中增加Web模板:
flutter create --template=plugin --platforms=web .
和Windows, Android不同,web模板没有生成一个叫web的目录,也没有生成任何的JavaScritp代码文件。我们只看到一个新的flutter_barcode_sdk_web.dart文件。
接下来把插件声明添加到pubspec.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
如何实现JavaScript和Dart交互
和其它平台一样,handleMethodCall()是入口:
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}\'',
);
}
}
但不同的是,web并不需要在插件中引入依赖库编译,我们要做的只是定义接口。
我定义了两个接口:decodeFile()和decodeVideo(),分别用于通过图像和通过视频来识别一维码和二维码。
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();
}
在视频模式下,结果是通过回调返回的。然而,在上层的flutter_barcode_sdk.dart中,回调函数引用没有办法通过invokeMethod传递下来。我的解决方法是使用全局变量保存回调函数:
Future<void> decodeVideo(Function callback) async {
globalCallback = callback;
await _channel.invokeMethod('decodeVideo');
}
为了避免和其它平台冲突,这个变量单独定义在一个global.dart 文件中:
Function globalCallback = () => {};
现在打开barcode_manager.dart,根据dynamsoft-javascript-barcode定义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);
}
为了实现JavaScript的Promise,我们需要在另外一个utils.dart文件中定义:
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);
接下来实现对象初始化:
/// 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的函数需要通过allowInterop()封装才能够被JavaScript调用。
实现decodeFile():
Future<List<Map<dynamic, dynamic>>> decodeFile(String filename) async {
List<dynamic> barcodeResults =
await handleThenable(_barcodeReader.decode(filename));
return _resultWrapper(barcodeResults);
}
实现decodeVideo()回调:
_barcodeScanner.onFrameRead = allowInterop((results) =>
{globalCallback(callbackResults(_resultWrapper(results)))});
创建Web一维码,二维码识别程序
创建一个新的Flutter工程,并在web/index.html中添加<script src="https://cdn.jsdelivr.net/npm/dynamsoft-javascript-barcode@8.2.3/dist/dbr.js" data-productKeys="PRODUCT-KEYS"></script>。
在pubspec.yaml中添加image_picker和flutter_barcode_sdk:
dependencies:
flutter_barcode_sdk:
image_picker:
在UI中添加两个按钮,一个用于加载图片,一个用于开启摄像头视频流:
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)});
}),
]),
),
])),
);
最后运行程序:
flutter run -d chrome
Flutter插件下载
https://pub.dev/packages/flutter_barcode_sdk
源码
关注公众号
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
PostgreSQL 同时发布多个版本更新
PostgreSQL13.3、12.7、11.12、10.17 和 9.6.22 现已发布,这些版本关闭了三个安全漏洞,并修复了过去三个月中报告的 45 个以上的 bug。 主要更新内容 关闭 CVE-2021-32027:数组下标计算中的整数溢出导致缓冲区溢出。受影响的版本:9.6 - 13。在修改某些 SQL 数组值时,缺失的边界检查让认证的数据库用户向服务器内存的大范围内写入任意字节。 关闭 CVE-2021-32028:INSERT ... ON CONFLICT ... DO UPDATE 中的内存泄露。受影响的版本:9.6 - 13。该功能首次出现在9.5中。在一个特意制作的表中使用 INSERT ... ON CONFLICT ... DO UPDATE 命令,攻击者可以读取服务器内存的任意字节。在默认配置中,任何经过认证的数据库用户都可以创建先决条件的对象,并完成这种攻击。 关闭 CVE-2021-32029:分区表 UPDATE ... RETURNING 中的内存泄露。受影响的版本: 11 - 13。在一个特意制作的分区表上使用 UPDATE ... RETURNI...
-
下一篇
SigNoz —— React 和 Go 编写的应用可观察性平台
SigNoz 是一个开源的应用可观察性平台。 SigNoz 是用 React 和 Go 编写的, 帮助开发者使用最小的精力快速实现他们的可观测性目标。 使用Kafka和流处理器实时摄取大量可观测性数据。然后,这些数据将传递给Apache Druid,该Druid擅长存储此类数据,用于短期和长期SQL分析。当数据被扁平化并存储在 Druid 中,SigNoz 的查询服务可以查询并将数据传递给 SigNoz React 前端。然后,前端为用户创建漂亮的图表,使可观察性数据可视化. SigNoz 能够以行业标准格式获取数据,包括 Jaeger、Zipkin 和 OpenConsensus。之后,收集的数据被转发到 Apache Kafka。 SigNoz 将几个组件捆绑在一起,创建了一个可扩展的、耦合松散的系统,很容易上手使用。其中一些最重要的组件有: OpenTelemetry Collector Apache Kafka Apache Druid
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Linux系统CentOS6、CentOS7手动修改IP地址
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- 2048小游戏-低调大师作品
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- 面试大杂烩



微信收款码
支付宝收款码