Flutter Web 的資料真的是太少了啊,可以說(shuō)是幾乎沒(méi)有。
Flutter Web 很多庫(kù)基本都用不了映皆。比如 WebView 的庫(kù),與JS交互准脂,使用原生的方法根本走不通劫扒。
瀏覽器的項(xiàng)目還是要用前端JS的方式解決。經(jīng)過(guò)了一通折騰狸膏,皇天不負(fù)苦心人沟饥,終于是把路走通了。
目錄
1. 與原生JS交互
1.1 Flutter 調(diào)用 Js湾戳;
1.2 Js 調(diào)用 Flutter贤旷;
2. 與React交互
2.1 Flutter 調(diào)用 React 的 Js 方法;
2.2 React 調(diào)用 Flutter 的方法砾脑。
先引入 Flutter 的 JS 庫(kù):https://pub.dev/packages/js
dependencies:
js: ^0.6.7
一幼驶、與原生JS交互
1. Flutter 調(diào)用 Js
1.1 先在 HTML 提供一個(gè) JS 方法:
js 的代碼可以寫(xiě)在 index.html 的代碼里:
方法如下:
function getPersonInfo() {
const Person = {
name: 'John',
sex: 'women',
age: 36,
}
const jsonString = JSON.stringify(Person)
return jsonString;
}
1.2 在 Flutter 調(diào)用剛才的 JS 方法:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'dart:js' as js;
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ElevatedButton(
child: const Text("調(diào)用js代碼"),
onPressed: () {
// Flutter Web 調(diào)用 Js
var personInfo = js.context.callMethod('getPersonInfo');
print("personInfo is $personInfo");
},
)
);
}
}
至此,把 Flutter Web 項(xiàng)目運(yùn)行一下韧衣,就可以在控制臺(tái)看到運(yùn)行結(jié)果盅藻。
非常簡(jiǎn)單购桑。
2. Js 調(diào)用 Flutter 方法
先通過(guò) Flutter 在 js 的 window 中注入一個(gè)方法,然后在 js 中調(diào)用這個(gè)方法氏淑。
2.1 先在 Flutter 提供一個(gè)方法(詳見(jiàn)注釋?zhuān)?/h4>
import 'dart:convert';
import 'package:flutter/material.dart';
import 'dart:js' as js;
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// Toast 顯示提示信息 - 提供給 JS 的方法
void toastMsg(String msg) {
print("address: $msg");
MyUtils.showToast(msg);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ElevatedButton(
child: const Text("調(diào)用js代碼"),
onPressed: () {
// 給 JS 提供一個(gè)方法勃蜘。 js 通過(guò) window 調(diào)用
js.context["toastMsg"] = toastMsg;
// Flutter Web 調(diào)用 Js
js.context.callMethod('showVersion');
},
)
);
}
}
2.2 Js端的代碼:
function showVersion() {
// 調(diào)用 Flutter 提供的方法
window.toastMsg("當(dāng)前版本號(hào)為:1.0.0");
}
import 'dart:convert';
import 'package:flutter/material.dart';
import 'dart:js' as js;
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// Toast 顯示提示信息 - 提供給 JS 的方法
void toastMsg(String msg) {
print("address: $msg");
MyUtils.showToast(msg);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ElevatedButton(
child: const Text("調(diào)用js代碼"),
onPressed: () {
// 給 JS 提供一個(gè)方法勃蜘。 js 通過(guò) window 調(diào)用
js.context["toastMsg"] = toastMsg;
// Flutter Web 調(diào)用 Js
js.context.callMethod('showVersion');
},
)
);
}
}
function showVersion() {
// 調(diào)用 Flutter 提供的方法
window.toastMsg("當(dāng)前版本號(hào)為:1.0.0");
}
至此,就實(shí)現(xiàn)了 Flutter 與 原生JS 的彼此交互假残。
原生JS基本只能實(shí)現(xiàn)最基礎(chǔ)的邏輯缭贡,只掌握到這一步幾乎毫無(wú)意義。目前絕大多數(shù)的項(xiàng)目都是 React 或 Vue 框架寫(xiě)的辉懒,React 的方法與原生JS還是有一些區(qū)別阳惹。接下來(lái)看一下與 React 的方法交互。
二眶俩、Flutter 與 React 交互
學(xué)習(xí)了上面的方法之后莹汤,應(yīng)該已經(jīng)明白了其中的原理。 真正做的時(shí)候仿便,就會(huì)發(fā)現(xiàn)有一個(gè)難點(diǎn):React框架打包之后体啰,會(huì)對(duì)之前的JS方法名進(jìn)行壓縮處理攒巍,之前的方法名就變了嗽仪,會(huì) not found。所以柒莉,這里主要處理的就是 React 打包之后如何調(diào)用原始的方法闻坚。
上面已經(jīng)知道如何互相交互了,這里就簡(jiǎn)單點(diǎn)兢孝,只列出關(guān)鍵點(diǎn)窿凤。
1. 在 React 中編寫(xiě)方法供 Flutter調(diào)用
React 可以做一些自己業(yè)務(wù),最終打包跨蟹,將打包之后的項(xiàng)目移至 Flutter Web 的項(xiàng)目中雳殊。
關(guān)鍵點(diǎn)如下:
window自定義聲明:
declare global {
interface Window {
toastMsg: Function;
}
}
關(guān)鍵方法:
const getAddress = async () => {
// ...
// 網(wǎng)絡(luò)請(qǐng)求
// ...
const ads = "請(qǐng)求結(jié)果";
setAddress(ads);
if (typeof window["toastMsg"] === "function") {
window.toastMsg(address);
}
return address ? address : "未登錄";
};
// 提供給 Flutter 的方法
window["getMyAddress"] = async () => {
return await getAddress();
};
2. 將 React 打包,把關(guān)鍵信息復(fù)制到 Flutter Web 項(xiàng)目中
2.1 React 打包
2.2 將這些文件復(fù)制
2.3 將關(guān)鍵文件復(fù)制到 Flutter 項(xiàng)目
2.4 在 Flutter 中調(diào)用方法
@JS()
external getMyAddress();
// 暴露給JS的方法
void toastMsg(String msg) {
print("address: $msg");
MyUtils.showToast(msg);
}
Widget buildMine() {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: () {
// 將方法暴露給 js 的 window
js.context["toastMsg"] = toastMsg;
// 調(diào)用 JS 方法
var personInfo = js.context.callMethod('getMyAddress');
print("getAddress is $personInfo");
},
child: Text("js2PersonInfo"),
),
SizedBox(height: 20),
TextButton(
onPressed: () async {
// 調(diào)用Js的方法
try {
var promise = getMyAddress();
var result = await promiseToFuture(promise); // 第三個(gè)坑
print("getAddress is $result");
} catch (e) {
print("getDeviceInfo錯(cuò)誤信息是 $e");
}
},
child: Text("getMyAddress"),
),
SizedBox(height: 20),
],
),
);
}
注意:
至此窗轩,就實(shí)現(xiàn)了 Flutter Web 與 React 的交互夯秃。列出關(guān)鍵點(diǎn)記錄,以便后續(xù)使用痢艺。
不僅單單是交互仓洼。
以此方式,還可以彌補(bǔ)一些 Flutter 非常大的一個(gè)劣勢(shì):網(wǎng)頁(yè)首次打開(kāi)堤舒,非常緩慢色建。
可以使用 React 實(shí)現(xiàn)一些動(dòng)畫(huà),或者加載初始的界面舌缤,F(xiàn)lutter也會(huì)在同步加載箕戳。
結(jié)束某残。
如果有什么錯(cuò)誤,或者有更好的實(shí)現(xiàn)方式陵吸,歡迎討論驾锰。
參考:
- flutter web 與 js 通信: https://juejin.cn/post/7173627056205496333
- flutter web 與 js Promise 通信: https://juejin.cn/post/7173990644405239838
- 多終端自定義window方法: https://juejin.cn/post/6976654060430557220#heading-3