尝试使用 WebViewX

Jimmy a591021a6a some test 2 éve
.vscode f3d22eb61d 可用版本暂存 2 éve
android d80b6ea2d3 init repo 2 éve
ios d80b6ea2d3 init repo 2 éve
lib a591021a6a some test 2 éve
test d80b6ea2d3 init repo 2 éve
threejsview 0a62825489 优化 js 载入方式 2 éve
web a591021a6a some test 2 éve
windows d80b6ea2d3 init repo 2 éve
.gitignore d80b6ea2d3 init repo 2 éve
.metadata d80b6ea2d3 init repo 2 éve
README.md 2da563e37a 加入readme 2 éve
analysis_options.yaml d80b6ea2d3 init repo 2 éve
pubspec.lock d80b6ea2d3 init repo 2 éve
pubspec.yaml 0a62825489 优化 js 载入方式 2 éve

README.md

WebViewX Demo

adrianflutur/webviewx (github.com)

先启动 example 尝试一下加载一个外部带 threejs 示例的网站

基本没有问题,交互也很流畅 唯一要注意的一点就是,在webview上面不能覆盖flutter组件,会导致无法点击

载入 Flutter 内 assets 下的 html 文件

首先将 html 文件放入 assets 文件夹下,然后调用 loadContent 方法就可以载入了,但是有个限制就是 html 文件内的 <script> 标签不能获取到本地的 js 文件

webviewController.loadContent(
  'threejsview/MainPage.html',
  SourceType.html,
  fromAssets: true,
);

想要加载自己的 js 文件,还需要手动传入

/// A set of [EmbeddedJsContent].
///
/// You can define JS functions, which will be embedded into
/// the HTML source (won't do anything on URL) and you can later call them
/// using the controller.
///
/// For more info, see [EmbeddedJsContent].
@override
final Set<EmbeddedJsContent> jsContent; // 此变量接受一串 js 文本集合

一般可以这样将 js 传入 🚧【但是此方式存在问题,initState 中的异步操作不会执行】

/// 错误示例 🚧
final javaScriptsFromAssets = [];

void initState() {
    ///...
	final ScriptsList = [
	      "threejsview/js/build/three.js",
	      "...",
	    ];
	for (String path in ScriptsList) {
	  _loadJSFromAssets(path).then((value) {
		javaScriptsFromAssets.add(value);
	  });
	}
}

///buildWebViewX 时候这样传入
Widget _buildWebViewX() {
  return WebViewX(
	//...
	jsContent: javaScriptsFromAssets.map((e) => EmbeddedJsContent(js: e)).toSet(),
  );
}

需要改用 FutureBuilder

Future<String> _preloadJS() async {
    final scriptsList = [
      "threejsview/js/build/three.js",
      ///...
    ];
    for (String path in scriptsList) {
      var value = await _loadJSFromAssets(path);
      javaScriptsFromAssets.add(value);
    }
    return Future.value("");
  }

Widget _buildWebViewX() {
return FutureBuilder<String>(
	future: _preloadJS(),
	builder: (context, AsyncSnapshot<String> snapshot) {
	  if (snapshot.hasData) {
		return WebViewX(
		  ///...
		  jsContent: javaScriptsFromAssets
			  .map((e) => EmbeddedJsContent(js: e))
			  .toSet(),
		  ///...
		);
	  } else {
		return const CircularProgressIndicator();
	  }
	});
}

这里有两种类型的 JS 传入,js 是一种 webJsmobileJs 是另一种

  • js 是注入与平台无关的代码
  • webJsmobileJs 是根据平台注入对应的代码

关于 Assets 内资源文件打包的说明

需要在 Dart 代码下获取的文件需要写入 pubspec.yaml 来进行打包

flutter:
  uses-material-design: true
  assets:
    - threejsview/
    - threejsview/js/gui/
    - threejsview/js/customScript/
    - threejsview/js/customScript/utility/
    - threejsview/js/customScript/manager/
    - threejsview/js/customScript/clipWorkflow/
    - threejsview/js/controls/
    - threejsview/js/build/

但是在 JS 代码下获取的文件,不需要写入配置,只要将文件放入项目根目录下的 web 文件夹即可,因为 JS 执行的资源文件获取是从 build 文件夹根目录获取的,而 Dart 打包的资源文件会自动放入 assets 文件夹下,在 Dart 获取 assets 内的文件时,路径头 assets/ 是缺省值

Flutter 内的 Dart 触发 Webview 内部 JS 方法

在 webviewX 插件下,外部调用内部方法只需要这样写

webviewController.callJsMethod("method_name",["paramA","paramsB"])

也可以用一个变量来接受 js 函数的返回值

var resultFromJs = await webviewController.callJsMethod("method_name",["paramA","paramsB"])
print(resultFromJs);

Webview 内的 JS 触发 Flutter 内部 Dart 方法

首先在 _buildWebViewX 时需要定义好 DartCallback

dartCallBacks: {
	DartCallback(
	  name: 'TestDartCallback',
	  callBack: (msg) => showSnackBar(msg.toString(), context),
	),
	DartCallback(
	  name: 'Dart_GetClipPlaneData',
	  callBack: (msg) {
		print("WebView 触发 Dart_GetClipPlaneData ${msg.toString()}");
		final result = {'ErrorCode': "1001"};
		return jsonEncode(result);
	  },
	)
  },

参数的传递可用利用 JSON 序列化 Dart Object 或者 JS Object webview 下直接通过定义好的函数名就可以触发

let inputData = {
	PointsList: mappingPoints,
	ClipImageName: imageName
};
let json = Dart_GetClipPlaneData(JSON.stringify(inputData));
return JSON.parse(json);

可以通过参数传入 Base64 编码的图像,在 threejs 中作为纹理贴图

final faceImages = [
  _loadRandomImage(500, 500),
  _loadRandomImage(500, 500),
  _loadRandomImage(500, 500),
  _loadRandomImage(500, 500),
  _loadRandomImage(500, 500),
  _loadRandomImage(500, 500),
];
// print([759, 596, 397, ...faceImages]);
try {
  await webviewController
	  .callJsMethod('changeSurface', [759, 596, 397, ...faceImages]);
} catch (e) {
  showAlertDialog(
	executeJsErrorMessage,
	context,
  );
}