這幾天剛研究用完了UniApp和flutter項(xiàng)目的交互,F(xiàn)lutterWeb已經(jīng)逐漸穩(wěn)定下來,想到以后可能會有Flutter項(xiàng)目和Flutter Web項(xiàng)目的交互,所以研究了一下交互方式。
困擾我最大的問題就是,F(xiàn)lutter Web(以下簡稱web項(xiàng)目)以dart的寫法,如何去和Flutter交互,如果依然是前端代碼交互也就算了,至少還有個(gè)js的樣子,可是flutter的web項(xiàng)目和uniapp或者vue完全不同,完全是另一套寫法,讓人摸不到頭腦。說到底就是要解決一個(gè)問題,寫的方法如何相互調(diào)用?
目的
我們要做的事情是什么?
在flutter項(xiàng)目中以嵌入h5的方式打開web項(xiàng)目,并且進(jìn)行交互。交互具體內(nèi)容是
點(diǎn)擊web項(xiàng)目中的獲取token按鈕,獲取到存放在flutter項(xiàng)目中的token,并且彈窗顯示出來。
如果你有類似的需求,希望接下來的內(nèi)容能讓你找到答案
預(yù)備工作
看本篇內(nèi)容需要掌握,flutter項(xiàng)目和js相互調(diào)用
可以參考這兩篇內(nèi)容:
http://www.itdecent.cn/p/86916cab2cf3
https://www.cnblogs.com/lizhanqi/p/13502763.html
簡單來說我們要用的知識點(diǎn),如何把自己寫在web項(xiàng)目的方法,可以在js中調(diào)用,就像是flutter項(xiàng)目調(diào)用其他js方法那樣調(diào)用。
import 'dart:js' as js;
void testMethod(){
// do something
}
// 起到一個(gè)類似注冊的作用,這樣在js的上下文中,
// 就可以使用testMethod方法了,沒有這么寫的話,
// 直接調(diào)用會告知未找到方法。
// 同時(shí)也意味著,flutter項(xiàng)目可以使用webview_flutter插件
// 提供的evaluateJavascript方法調(diào)用到這個(gè)方法了
js.context["testMethod"] = testMethod
開發(fā)環(huán)境:
flutter 2.2.3
開始
接下來接直接以web項(xiàng)目來說了。
這事涉及到兩個(gè)項(xiàng)目,我們先來創(chuàng)建好兩個(gè)項(xiàng)目
我分別創(chuàng)建了
simple_webview_project
simple_web_project
再次明確一下我們要做的事情:
現(xiàn)在我們要達(dá)到的目的是在webview項(xiàng)目中以嵌入h5的方式打開web項(xiàng)目,并且進(jìn)行交互。交互具體內(nèi)容是
點(diǎn)擊web項(xiàng)目中的獲取token按鈕,獲取到存放在webview項(xiàng)目中的token,并且彈窗顯示出來。
web項(xiàng)目
首先來看web項(xiàng)目:
和其他web框架一樣,首先需要寫兩個(gè)方法,一個(gè)是用來調(diào)用webview項(xiàng)目的方法, 一個(gè)是用來讓webview調(diào)用的方法。
通過
// 點(diǎn)擊獲取Token,完成和flutter項(xiàng)目的交互(調(diào)用webview項(xiàng)目方法)
void getToken() {
js.context.callMethod("callFlutterMethod", [
json.encode({
"api": "getToken",
"data": {
"name": 'getToken',
"needCallback": true,
"needToken": true,
"callbackName": 'getTokenCallback',
"callbackArgs": 'msg'
},
})
]);
}
// 這里是讓flutter調(diào)用的回調(diào)方法。(用來讓webview調(diào)用的方法)
void getTokenCallback(msg, token) {
showDialog(
context: context,
builder: (c) {
return AlertDialog(
title: Text(token),
);
});
}
需要特別說明的一點(diǎn)是 callFlutterMethod 這個(gè)方法是不存在的,是自己寫的,寫在一個(gè)js文件中,然后在web項(xiàng)目中的index.html中引入。方法內(nèi)容很簡單,
// 就是起到一個(gè)橋接的作用。我也嘗試過,js.context["nativeBridge"].callMethod,但是調(diào)用被告知未找到這個(gè)對象。
// 因?yàn)閚ativeBridge是flutter項(xiàng)目來管理的,我分析可能是初始化時(shí)機(jī)的問題,望有懂的大佬,不吝賜教。
function callFlutterMethod(args){
nativeBridge.postMessage(args)
}
webview 項(xiàng)目
webview項(xiàng)目需要做的事情多一點(diǎn),主要是要寫webview的內(nèi)容,和交互操作。就像服務(wù)端一樣,臟活累活都是服務(wù)端來干。
如果你之前有過和其他項(xiàng)目交互,那么什么都不用改,直接用就行。這里發(fā)一下我用的。
// 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創(chuàng)建完成后會產(chǎn)生一個(gè) 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; //來源于當(dāng)前widget, 便于操作UI
Future<WebViewController> _controller; //當(dāng)前webView 的 controller
NativeBridge(this.context, this._controller);
// api 與具體函數(shù)的映射表,可通過 _functions[key](data) 調(diào)用函數(shù)
get _functions => <String, Function>{"getToken": _getToken};
@override
String get name =>
"nativeBridge"; // js 通過 nativeBridge.postMessage(msg); 調(diào)用flutter
// 處理js請求
@override
get onMessageReceived => (msg) async {
// 將收到的string數(shù)據(jù)轉(zhuǎn)為json
Map<String, dynamic> message = json.decode(msg.message);
// 異步是因?yàn)橛行゛pi函數(shù)實(shí)現(xiàn)可能為異步,如inputText,等待UI相應(yīng)
// 根據(jù) api 字段,調(diào)用具體函數(shù)
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項(xiàng)目如何部署,這里就不說了,大家各憑本事吧。我這里用的是springboot。
打web包的時(shí)候 要指定渲染器,否則的話中文渲染不出來,而且會一直報(bào)錯(cuò)。
但是在pc端沒問題,原因是pc使用的渲染器是canvaskit本身就可以。
但是手機(jī)默認(rèn)是使用html渲染,為什么中文會報(bào)錯(cuò),還不知道,但是在統(tǒng)一了渲染器之后,中文就可以了,
這是打包命令:
flutter build web --web-renderer canvaskit --release
附上demo鏈接,github有時(shí)候連不上,就放gitee上了。
https://gitee.com/gotosleep7/share.git