Flutter项目和Flutter Web交互

2021-10-12  本文已影响0人  Chg_43da

这几天刚研究用完了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

上一篇下一篇

猜你喜欢

热点阅读