Flutter JavaScript交互插件js_wrapping的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter JavaScript交互插件 js_wrapping 的使用

js_wrapping 是一个允许开发者为 JavaScript 对象定义良好类型的接口的 Dart 包。通过这个包,你可以创建与 JavaScript 对象交互的 Dart 类,并且这些类具有明确的 Dart API,包括类型注释、构造函数,甚至可选和命名参数。

编写 JS Wrapper

假设有一个 JavaScript 类如下:

LatLng = function(lat, lng) {
  this.lat = lat;
  this.lng = lng;
}
LatLng.prototype.equals = function(other) {
  return this.lat === other.lat && this.lng === other.lng;
}

你可以创建一个包装类如下:

@JsName()
abstract class LatLng {
  factory LatLng(num lat, num lng, [bool noWrap]) => $js;

  bool equals(LatLng other);
  num get lat => _lat();
  @JsName('lat')
  num _lat();
  num get lng => _lng();
  @JsName('lng')
  num _lng();
  String toString();
  String toUrlValue([num precision]);
}

一旦生成器执行完毕,你就可以使用 LatLng 来包装一个 JavaScript 的 LatLng 对象。

配置和初始化

添加依赖

在你的 pubspec.yaml 文件中添加以下内容:

dependencies:
  js_wrapping: ^0.6.0
dev_dependencies:
  js_wrapping_generator: ^0.6.0

运行生成器

参考 source_gen package 的运行生成器部分。

使用方法

定义 Typed JavaScript Interfaces

要创建一个 Typed JavaScript Interface,你需要从创建一个标记有 @JsName() 的类开始。它将作为创建 JavaScript 接口的模板。

@JS('google.maps')
library google_maps.sample.simple;

import 'package:js_wrapping/js_wrapping.dart';

@JsName()
abstract class LatLng {
  factory LatLng(num lat, num lng, [bool noWrap]) => $js;

  bool equals(LatLng other);
  num get lat => _lat();
  @JsName('lat')
  num _lat();
  num get lng => _lng();
  @JsName('lng')
  num _lng();
  String toString();
  String toUrlValue([num precision]);
}

生成器会生成一个新的库 mylib.g.dart,包含以下内容:

// GENERATED CODE - DO NOT MODIFY BY HAND

// **************************************************************************
// JsWrappingGenerator
// **************************************************************************

// Copyright (c) 2015, Alexandre Ardhuin. All rights reserved. Use of this
// source code is governed by a BSD-style license that can be found in the
// LICENSE file.

@JS('google.maps')
library google_maps.sample.simple;

import 'package:js_wrapping/js_wrapping.dart';

@JS()
class LatLng {
  external LatLng(num lat, num lng, [bool noWrap]);

  external bool equals(LatLng other);

  external String toString();

  external String toUrlValue([num precision]);
}

extension LatLng$Ext on LatLng {
  num get lat => _lat();
  num get lng => _lng();

  num _lat() => callMethod(this, 'lat', []);

  num _lng() => callMethod(this, 'lng', []);
}

构造函数用于创建 JS 对象

如果 LatLng 是一个 JavaScript 对象/函数,你可以在 JavaScript 中用 LatLng() 创建一个新实例。为了使 Dart 侧能够创建这样的 JavaScript 实例,你需要定义一个 factory 构造函数:

@JsName()
abstract class LatLng {
  factory LatLng(num lat, num lng, [bool noWrap]) => $js;
}

这将生成:

@JS()
class LatLng {
  external LatLng(num lat, num lng, [bool noWrap]);
}

现在可以通过 LatLng() 在 Dart 中实例化 JavaScript 对象。

属性和访问器

可以向类中添加属性或抽象的 getter/setter,并生成访问底层 JavaScript 对象属性的 getter 和 setter。

@JsName()
abstract class Person {
  factory Person() => $js;
  String firstname, lastname;
  int get age;
  void set email(String email);
}

这将生成:

@JS()
class Person {
  external Person();
  external String get firstname;
  external set firstname(String value);
  external String get lastname;
  external set lastname(String value);
  external int get age;
  external void set email(String email);
}

方法

抽象方法将以相同的方式实现:

@JsName()
abstract class Person {
  factory Person() => $js;
  String sayHelloTo(String other);
  void fall();
}

这将生成:

@JS()
class Person {
  external Person();

  external String sayHelloTo(String other);

  external void fall();
}

名称使用

构造函数

你可以通过在类上提供 JsName('MyClassName') 来覆盖名称:

@JsName('People')
abstract class Person {
  factory Person() => $js;
  String sayHelloTo(Person other);
  Person get father;
}

成员

你可以通过在成员上提供 JsName('myMemberName') 来覆盖名称:

@JsName()
abstract class Person {
  @JsName('daddy') Person get father;
}

小技巧和窍门

匿名对象

实例化匿名 JavaScript 对象是很常见的。如果你的私有类映射到一个匿名对象,可以在其上添加 @anonymous 注解:

@JsName()
@anonymous
abstract class Foo {
  factory Foo() => $js;
}

这将生成:

@JS()
@anonymous
class Foo {
  external factory Foo();
}

从方法创建 getter

如果一个 JavaScript 对象有一个 getXxx() 函数,你想在 Dart 端将其映射为 get xxx,你可以这样做:

@JsName()
abstract class Person {
  String get firstname => _getFirstname();
  @JsName('getFirstname')
  String _getFirstname();
}

这个方法可以应用于任何你想进行的重定向。

示例 Demo

下面是一个完整的示例 demo,展示了如何使用 js_wrapping 插件来与 JavaScript 进行交互:

JavaScript 代码

function LatLng(lat, lng) {
  this.lat = lat;
  this.lng = lng;
}

LatLng.prototype.equals = function(other) {
  return this.lat === other.lat && this.lng === other.lng;
};

LatLng.prototype.toString = function() {
  return `Lat: ${this.lat}, Lng: ${this.lng}`;
};

Dart 代码

首先,在 pubspec.yaml 中添加依赖:

dependencies:
  js_wrapping: ^0.6.0
dev_dependencies:
  js_wrapping_generator: ^0.6.0

然后,创建一个 Dart 文件(例如 lat_lng.dart):

import 'package:js_wrapping/js_wrapping.dart';

@JsName()
abstract class LatLng {
  factory LatLng(num lat, num lng) => $js;

  bool equals(LatLng other);
  num get lat => _lat();
  @JsName('lat')
  num _lat();
  num get lng => _lng();
  @JsName('lng')
  num _lng();
  String toString();
}

接下来,运行生成器以生成相应的 Dart 代码:

flutter pub run build_runner build

最后,在你的 Flutter 应用中使用这个类:

import 'package:flutter/material.dart';
import 'lat_lng.g.dart'; // 生成的文件

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    final latLng = LatLng(39.9042, 116.4074); // 创建一个 LatLng 实例
    print(latLng.toString()); // 输出: Lat: 39.9042, Lng: 116.4074

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('js_wrapping Demo')),
        body: Center(child: Text('Check console for output')),
      ),
    );
  }
}

更多关于Flutter JavaScript交互插件js_wrapping的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter JavaScript交互插件js_wrapping的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter中使用js_wrapping插件与JavaScript进行交互的代码示例。js_wrapping允许Flutter应用通过平台通道与嵌入的WebView中的JavaScript代码进行通信。不过需要注意的是,js_wrapping可能不是一个实际存在的插件名称,这里我们假设有一个类似功能的插件,或者你可能需要参考一些类似功能的插件如flutter_webview_pluginwebview_flutter并结合平台通道来实现类似功能。

由于js_wrapping可能不是标准插件,以下示例将使用webview_flutter和平台通道来模拟这种交互。

1. 添加依赖

首先,在pubspec.yaml文件中添加webview_flutter依赖:

dependencies:
  flutter:
    sdk: flutter
  webview_flutter: ^3.0.4  # 请检查最新版本号

2. 创建Flutter WebView页面

创建一个新的Flutter页面,包含一个WebView组件,并设置JavaScript通道的监听器。

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'dart:ui' as ui;

class WebViewPage extends StatefulWidget {
  @override
  _WebViewPageState createState() => _WebViewPageState();
}

class _WebViewPageState extends State<WebViewPage> {
  late WebViewController _controller;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('WebView with JavaScript Interaction'),
      ),
      body: WebView(
        initialUrl: 'about:blank',
        javascriptMode: JavascriptMode.unrestricted,
        onWebViewCreated: (WebViewController webViewController) {
          _controller = webViewController;
          _loadHtmlFromAssets();
        },
        onPageFinished: (String url) {
          _controller.evaluateJavascript('''
            window.Flutter.postMessage = function(message) {
              nativeChannel.postMessage(JSON.stringify(message));
            };
          ''');

          // 监听来自JavaScript的消息
          _controller.addJavascriptChannel(
            JavascriptChannel(
              name: 'nativeChannel',
              onMessageReceived: (JavascriptMessage message) {
                print('Received message from JavaScript: ${message.message}');
                // 在这里处理从JavaScript接收到的消息
              },
            ).onDispose: () {
              // 清理逻辑
            },
          );
        },
      ),
    );
  }

  _loadHtmlFromAssets() async {
    String htmlContent = await rootBundle.loadString('assets/sample.html');
    _controller.loadUrl(Uri.dataFromString(
      htmlContent,
      mimeType: 'text/html',
      encoding: Encoding.getByName('utf-8')
    ).toString());
  }
}

3. 创建HTML文件

assets文件夹中创建一个名为sample.html的文件,并添加以下内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sample HTML</title>
    <script>
        function sendMessageToFlutter() {
            const message = {
                type: 'greeting',
                text: 'Hello from JavaScript!'
            };
            window.Flutter.postMessage(message);
        }
    </script>
</head>
<body>
    <h1>Hello from WebView</h1>
    <button onclick="sendMessageToFlutter()">Send Message to Flutter</button>
</body>
</html>

4. 更新pubspec.yaml以包含assets

确保在pubspec.yaml中声明了assets文件夹:

flutter:
  assets:
    - assets/sample.html

5. 运行应用

现在,你可以运行你的Flutter应用,点击WebView中的按钮,应该会看到控制台输出从JavaScript发送的消息。

这个示例展示了如何使用webview_flutter插件在Flutter应用中嵌入一个WebView,并通过平台通道与JavaScript代码进行双向通信。如果你有一个特定的js_wrapping插件,请参考其官方文档进行相应调整。

回到顶部