Flutter Web 的资料真的是太少了啊,可以说是几乎没有。

Flutter Web 很多库基本都用不了。比如 WebView 的库,与JS交互,使用原生的方法根本走不通。

浏览器的项目还是要用前端JS的方式解决。经过了一通折腾,皇天不负苦心人,终于是把路走通了。

目录
1. 与原生JS交互
    1.1 Flutter 调用 Js;
    1.2 Js 调用 Flutter;
2. 与React交互
    2.1 Flutter 调用 React 的 Js 方法;
    2.2 React 调用 Flutter 的方法。

先引入 Flutter 的 JS 库:https://pub.dev/packages/js

dependencies:
  js: ^0.6.7

一、与原生JS交互

1. Flutter 调用 Js

1.1 先在 HTML 提供一个 JS 方法:

js 的代码可以写在 index.html 的代码里:

方法如下:

function getPersonInfo() {
    const Person = {
        name: 'John',
        sex: 'women',
        age: 36,
    }
    const jsonString = JSON.stringify(Person)
    return jsonString;
}

1.2 在 Flutter 调用刚才的 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("调用js代码"),
        onPressed: () {
          // Flutter Web 调用 Js
          var personInfo = js.context.callMethod('getPersonInfo');
          print("personInfo is $personInfo");
        },
      )
    );
  }
}

至此,把 Flutter Web 项目运行一下,就可以在控制台看到运行结果。
非常简单。

2. Js 调用 Flutter 方法

先通过 Flutter 在 js 的 window 中注入一个方法,然后在 js 中调用这个方法。

2.1 先在 Flutter 提供一个方法(详见注释):

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("调用js代码"),
        onPressed: () {
          // 给 JS 提供一个方法。 js 通过 window 调用
          js.context["toastMsg"] = toastMsg;

          // Flutter Web 调用 Js
          js.context.callMethod('showVersion');
        },
      )
    );
  }
}

2.2 Js端的代码:

function showVersion() {
    // 调用 Flutter 提供的方法
    window.toastMsg("当前版本号为:1.0.0");
}

至此,就实现了 Flutter 与 原生JS 的彼此交互。


原生JS基本只能实现最基础的逻辑,只掌握到这一步几乎毫无意义。目前绝大多数的项目都是 React 或 Vue 框架写的,React 的方法与原生JS还是有一些区别。接下来看一下与 React 的方法交互。

二、Flutter 与 React 交互

学习了上面的方法之后,应该已经明白了其中的原理。 真正做的时候,就会发现有一个难点:React框架打包之后,会对之前的JS方法名进行压缩处理,之前的方法名就变了,会 not found。所以,这里主要处理的就是 React 打包之后如何调用原始的方法。

上面已经知道如何互相交互了,这里就简单点,只列出关键点。

1. 在 React 中编写方法供 Flutter调用

React 可以做一些自己业务,最终打包,将打包之后的项目移至 Flutter Web 的项目中。

关键点如下:

window自定义声明:

declare global {
    interface Window {
        toastMsg: Function;
    }
}

关键方法:

const getAddress = async () => {
    // ...
    // 网络请求
    // ...
    const  ads = "请求结果";
    setAddress(ads);

    if (typeof window["toastMsg"] === "function") {
        window.toastMsg(address);
    }

    return address ? address : "未登录";
};

// 提供给 Flutter 的方法
window["getMyAddress"] = async () => {
    return await getAddress();
};

2. 将 React 打包,把关键信息复制到 Flutter Web 项目中

2.1 React 打包

2.2 将这些文件复制

2.3 将关键文件复制到 Flutter 项目

2.4 在 Flutter 中调用方法

@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;

            // 调用 JS 方法
            var personInfo = js.context.callMethod('getMyAddress');
            print("getAddress is $personInfo");
          },
          child: Text("js2PersonInfo"),
        ),
        SizedBox(height: 20),
        TextButton(
          onPressed: () async {
            // 调用Js的方法
            try {
              var promise = getMyAddress();
              var result = await promiseToFuture(promise); // 第三个坑
              print("getAddress is $result");
            } catch (e) {
              print("getDeviceInfo错误信息是 $e");
            }
          },
          child: Text("getMyAddress"),
        ),
        SizedBox(height: 20),
      ],
    ),
  );
}

注意:

至此,就实现了 Flutter Web 与 React 的交互。列出关键点记录,以便后续使用。


不仅单单是交互。

以此方式,还可以弥补一些 Flutter 非常大的一个劣势:网页首次打开,非常缓慢。

可以使用 React 实现一些动画,或者加载初始的界面,Flutter也会在同步加载。

结束。

如果有什么错误,或者有更好的实现方式,欢迎讨论。

参考: