Flutter项目和Flutter Web交互
这几天刚研究用完了UniApp和flutter项目的交互,FlutterWeb已经逐渐稳定下来,想到以后可能会有Flutter项目和Flutter Web项目的交互,所以研究了一下交互方式。
困扰我最大的问题就是,Flutter Web(以下简称web项目)以dart的写法,如何去和Flutter交互,如果依然是前端代码交互也就算了,至少还有个js的样子,可是flutter的web项目和uniapp或者vue完全不同,完全是另一套写法,让人摸不到头脑。说到底就是要解决一个问题,写的方法如何相互调用?
目的
我们要做的事情是什么?
在flutter项目中以嵌入h5的方式打开web项目,并且进行交互。交互具体内容是
点击web项目中的获取token按钮,获取到存放在flutter项目中的token,并且弹窗显示出来。
如果你有类似的需求,希望接下来的内容能让你找到答案
预备工作
看本篇内容需要掌握,flutter项目和js相互调用
可以参考这两篇内容:
https://www.jianshu.com/p/86916cab2cf3
https://www.cnblogs.com/lizhanqi/p/13502763.html
简单来说我们要用的知识点,如何把自己写在web项目的方法,可以在js中调用,就像是flutter项目调用其他js方法那样调用。
import 'dart:js' as js;
void testMethod(){
// do something
}
// 起到一个类似注册的作用,这样在js的上下文中,
// 就可以使用testMethod方法了,没有这么写的话,
// 直接调用会告知未找到方法。
// 同时也意味着,flutter项目可以使用webview_flutter插件
// 提供的evaluateJavascript方法调用到这个方法了
js.context["testMethod"] = testMethod
开发环境:
flutter 2.2.3
开始
接下来接直接以web项目来说了。
这事涉及到两个项目,我们先来创建好两个项目
我分别创建了
simple_webview_project
simple_web_project
再次明确一下我们要做的事情:
现在我们要达到的目的是在webview项目中以嵌入h5的方式打开web项目,并且进行交互。交互具体内容是
点击web项目中的获取token按钮,获取到存放在webview项目中的token,并且弹窗显示出来。
web项目
首先来看web项目:
和其他web框架一样,首先需要写两个方法,一个是用来调用webview项目的方法, 一个是用来让webview调用的方法。
通过
// 点击获取Token,完成和flutter项目的交互(调用webview项目方法)
void getToken() {
js.context.callMethod("callFlutterMethod", [
json.encode({
"api": "getToken",
"data": {
"name": 'getToken',
"needCallback": true,
"needToken": true,
"callbackName": 'getTokenCallback',
"callbackArgs": 'msg'
},
})
]);
}
// 这里是让flutter调用的回调方法。(用来让webview调用的方法)
void getTokenCallback(msg, token) {
showDialog(
context: context,
builder: (c) {
return AlertDialog(
title: Text(token),
);
});
}
需要特别说明的一点是 callFlutterMethod 这个方法是不存在的,是自己写的,写在一个js文件中,然后在web项目中的index.html中引入。方法内容很简单,
// 就是起到一个桥接的作用。我也尝试过,js.context["nativeBridge"].callMethod,但是调用被告知未找到这个对象。
// 因为nativeBridge是flutter项目来管理的,我分析可能是初始化时机的问题,望有懂的大佬,不吝赐教。
function callFlutterMethod(args){
nativeBridge.postMessage(args)
}
webview 项目
webview项目需要做的事情多一点,主要是要写webview的内容,和交互操作。就像服务端一样,脏活累活都是服务端来干。
如果你之前有过和其他项目交互,那么什么都不用改,直接用就行。这里发一下我用的。
// webview组件
import 'dart:async';
import 'dart:io';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'js_flutter.dart';
class WebViewPage extends StatefulWidget {
static String routeName = "/web_view";
String url;
WebViewPage(this.url, {Key? key}) : super(key: key);
@override
_WebViewPageState createState() => _WebViewPageState();
}
class _WebViewPageState extends State<WebViewPage> {
final _webViewController = Completer<WebViewController>();
@override
void initState() {
super.initState();
// Enable hybrid composition.
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.close),
onPressed: () {
Navigator.pop(context);
},
),
),
body: WebView(
javascriptChannels:
[NativeBridge(context, _webViewController.future)].toSet(),
initialUrl: widget.url,
javascriptMode: JavascriptMode.unrestricted, // 使用JS没限制
onWebViewCreated: (WebViewController webViewController) {
// 在WebView创建完成后会产生一个 webViewController
_webViewController.complete(webViewController);
},
),
);
}
}
js交互类
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class NativeBridge implements JavascriptChannel {
BuildContext context; //来源于当前widget, 便于操作UI
Future<WebViewController> _controller; //当前webView 的 controller
NativeBridge(this.context, this._controller);
// api 与具体函数的映射表,可通过 _functions[key](data) 调用函数
get _functions => <String, Function>{"getToken": _getToken};
@override
String get name =>
"nativeBridge"; // js 通过 nativeBridge.postMessage(msg); 调用flutter
// 处理js请求
@override
get onMessageReceived => (msg) async {
// 将收到的string数据转为json
Map<String, dynamic> message = json.decode(msg.message);
// 异步是因为有些api函数实现可能为异步,如inputText,等待UI相应
// 根据 api 字段,调用具体函数
final data = await _functions[message["api"]](message["data"]);
};
//拿token
_getToken(data) async {
handlerCallback(data);
}
handlerCallback(data) {
if (data['needCallback']) {
var args = data['callbackArgs'];
if (data['needToken']) {
args = "'${data['callbackArgs']}','ttttttoken'";
}
doCallback(data['callbackName'], args);
}
}
doCallback(name, args) {
_controller.then((value) => value.evaluateJavascript("$name($args)"));
}
}
ps:web项目如何部署,这里就不说了,大家各凭本事吧。我这里用的是springboot。
打web包的时候 要指定渲染器,否则的话中文渲染不出来,而且会一直报错。
但是在pc端没问题,原因是pc使用的渲染器是canvaskit本身就可以。
但是手机默认是使用html渲染,为什么中文会报错,还不知道,但是在统一了渲染器之后,中文就可以了,
这是打包命令:
flutter build web --web-renderer canvaskit --release
附上demo链接,github有时候连不上,就放gitee上了。
https://gitee.com/gotosleep7/share.git