草庐IT

Flutter与Webview交互

花心小人参 2023-07-15 原文

flutter层不支持webview,加载网页的功能还需要借助控件来处理。

通过pub.dev搜索以及对比网上文章,发现了几个比较受欢迎的flutter webview插件;

这三种插件对比图(此图借鉴自网络)

这里详细介绍使用flutter_inappwebview5.3.2版本插件完成交互;

注意:flutter_inappwebview6.xx 版本flutter3.0以下不支持;

控件地址:https://pub.dev/packages/flutter_inappwebview

1.安装插件

1)在配置文件pubbspec.yaml中加 flutter_inappwebview: 5.3.2;然后运行flutter pub get同步插件;

  flutter_inappwebview: 5.3.2

2.配置

1)在入口文件main.dart中加入

 // 不加这个强制横/竖屏会报错
  WidgetsFlutterBinding.ensureInitialized();  

3.主要类概览

该插件主要提供了以下类:

  • InAppWebView :一个 Flutter 小部件,用于添加整合到 Flutter 部件树的内联原生 WebView。

  • HeadlessInAppWebView :该类表示处于 headless 模式的 WebView。它可以用来在后台运行 WebView,而无需将 InAppWebView 附加到部件树中。

  • CookieManager :这个类实现了一个单例对象(共享实例),管理 WebView 实例使用的 cookie。

  • WebStorageManager :该类生成一个管理 Web 存储(供 WebView 实例使用)的单例对象(共享实例)。

  • ......

4.使用

(1)加载webview视图

1)展示一个url的视图内容

flutter代码如下:

app上呈现的效果图:

2) 展示一个html视图内容

html代码如下:

flutter代码:

在app上呈现的效果:

3)在flutter代码中处理你url文件或html文件的某个demo结构

以html文件为例,我想要在箭头所指的demo结构处添加文字内容“12345678910”和一些样式

那么在flutter代码中添加

//在InAppWebView配置项中添加
   onPageCommitVisible: (inAppWebViewControlle, uri) async {
   //_loadPage是声明的一个全局bool类型变量
          if (_loadPage) {
            setState(() {
              _loadPage = false;
            });
            //获取到webview的所有html结构
            var fileHtmlContents = await webViewController!.getHtml();
            //找到带有某种唯一标识的demo结构并替换它
            fileHtmlContents = fileHtmlContents!.replaceAll(
              '<div class="flutter-view"></div>',
              "<div class='flutter-view' style='width: 200px; height: 200px;background-color: green;'>12345678910</div>",
            );
            //重新渲染结构
            webViewController!.loadData(data: fileHtmlContents);
          }
        },

效果:

(2)与js交互传参

在flutter代码中添加

    //添加在InAppWebView配置项里
    // InAppWebview中获取InAppWebViewController
        onWebViewCreated: (InAppWebViewController controller) {
          // 注册一个JS处理方法,名称为myHandlerName
          controller.addJavaScriptHandler(
              handlerName: 'myHandlerName',
              callback: (args) {
                // 打印js方传递过来的参数
                print('args=js方传递过来的参数============================$args');
                // 传给js方的参数
                // 可以传递你所需要的任意类型数据,数组、对象等
                return "flutter给js的数据";
              });
        },
 //这个方法可以打印js中的conse.log内容
 onConsoleMessage: (controller, consoleMessage) {
          //这里是打印来自于js的conse.log打印
          print("consoleMessage==来自于js的打印====$consoleMessage");
        },

在js代码中添加

       // 下面的"flutterInAppWebViewPlatformReady"为固定写法
       // "myHandlerName"与flutter中注册的JS处理方法名称一致
        window.addEventListener("flutterInAppWebViewPlatformReady", function () {
            window.flutter_inappwebview
                                               //可以传递你所需要的任意类型数据,数组、对象等
                .callHandler("myHandlerName", "这里是传给flutter的参数")
                .then(function (res) {
                    console.log("res========flutter给html的数据", res);
                })
        })

flutter控制台输出:

传递数据成功啦,可以开始你对这些数据的处理啦。

注意:每次修改了html文件代码都需要终止flutter项目进程重新启动,否则你将看不到你的改动效果。

(3)完整demo代码

flutter代码

import 'package:flutter/cupertino.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

class HomePage extends StatefulWidget {
  HomePage({Key? key, required this.url}) : super(key: key);
  String url;
  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool _loadPage = true;
  InAppWebViewController? webViewController;

  // GlobalKey可以获取到对应的Widget的State对象!
  // 当我们页面内容很多时,而需要改变的内容只有很少的一部分且在树的底层的时候,我们如何去实现增量更新?
  // 通常情况下有两种方式,第一种是通过方法的回调,去实现数据更新,第二种是通过GlobalKey,
  final GlobalKey webViewKey = GlobalKey();
  // webview配置
  InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
    // 跨平台配置
    crossPlatform: InAppWebViewOptions(
      useShouldOverrideUrlLoading: true,
      mediaPlaybackRequiresUserGesture: false,
    ),
    // android平台配置
    android: AndroidInAppWebViewOptions(
      //支持HybridComposition
      useHybridComposition: true,
    ),
    // ios平台配置
    ios: IOSInAppWebViewOptions(
      allowsInlineMediaPlayback: true,
    ),
  );
  @override
  Widget build(BuildContext context) {
    return Container(
      child: InAppWebView(
        key: webViewKey,
        initialFile: 'lib/assets/html/demo.html',
       // initialUrlRequest: URLRequest(url: Uri.parse("https://inappwebview.dev/")),
        initialOptions: options,
        // InAppWebview中获取InAppWebViewController
        onWebViewCreated: (InAppWebViewController controller) {
          webViewController = controller;
          // 注册一个JS处理方法,名称为myHandlerName
          controller.addJavaScriptHandler(
              handlerName: 'myHandlerName',
              callback: (args) {
                // 打印js方传递过来的参数
                print('args=js方传递过来的参数============================$args');
                // 传给js方的参数
                return "flutter给js的数据";
              });
        },
        onConsoleMessage: (controller, consoleMessage) {
          //这里是打印来自于js的conse.log打印
          print("consoleMessage==来自于js的打印====$consoleMessage");
        },
        onPageCommitVisible: (inAppWebViewControlle, uri) async {
          if (_loadPage) {
            setState(() {
              _loadPage = false;
            });
            var fileHtmlContents = await webViewController!.getHtml();
            fileHtmlContents = fileHtmlContents!.replaceAll(
              '<div class="flutter-view"></div>',
              "<div class='flutter-view' style='width: 200px; height: 200px;background-color: green;'>12345678910</div>",
            );
            webViewController!.loadData(data: fileHtmlContents);
          }
        },
      ),
    );
  }
}

html代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo</title>
    <script>
        // 下面的"flutterInAppWebViewPlatformReady"为固定写法
        // "myHandlerName"与flutter中注册的JS处理方法名称一致
        window.addEventListener("flutterInAppWebViewPlatformReady", function () {
            window.flutter_inappwebview
                .callHandler("myHandlerName", "这里是传给flutter的参数")
                .then(function (res) {
                    console.log("res========flutter给html的数据", res);
                })
        })
    </script>
</head>

<body>
    <h1>我是一个demo</h1>
    <h1>我是一个html文件</h1>
    <h1>我想展示在flutter应用里</h1>
    <div class="flutter-view"></div>
</body>

</html>

5.常见问题

下载完插件直接运行会报错,需要根据报错提示修改配置;

1)在项目文件-->android-->app-->build.gradle中,变量值固定数值参考报错信息,建议为大于等于报错提示信息中的数值。

android {
    compileSdkVersion 31
    
    defaultConfig {
        minSdkVersion 23
        targetSdkVersion 28
    }
}

有关Flutter与Webview交互的更多相关文章

  1. ruby-on-rails - 如何在 ruby​​ 交互式 shell 中有多行? - 2

    这可能是个愚蠢的问题。但是,我是一个新手......你怎么能在交互式ruby​​shell中有多行代码?好像你只能有一条长线。按回车键运行代码。无论如何我可以在不运行代码的情况下跳到下一行吗?再次抱歉,如果这是一个愚蠢的问题。谢谢。 最佳答案 这是一个例子:2.1.2:053>a=1=>12.1.2:054>b=2=>22.1.2:055>a+b=>32.1.2:056>ifa>b#Thecode‘if..."startsthedefinitionoftheconditionalstatement.2.1.2:057?>puts"f

  2. Flutter 环境变量配置和flutter doctor中的错误解决 - 2

    一、环境变量右键点击我的电脑-属性:然后找到环境变量 1.Android的SDK不在C盘的话需要额外配这个到用户环境变量:ANDROID_HOMED:\AndroidSDK2.然后在系统变量:Path中添加一条这样的值        D:\Flutter\flutter\bin             这个值写flutter包解压的实际地址即可 3.在系统变量中添加两个镜像变量:        变量名:FLUTTER_STORAGE_BASE_URL      变量值:https://storage.flutter-io.cn        变量名:PUB_HOSTED_URL      变量

  3. ruby - 如何与 Ruby 中的 Perl 程序交互? - 2

    据我了解,在Ruby和Perl之间没有“桥梁”可以让您直接从Ruby调用Perl函数。据我了解,要从Ruby调用Perl程序,只需将其放在反引号中(即result=`./helloWorld.pl`)。但是,这不允许与Perl程序交互(即您不能与提示交互或提供输入)。我的问题如下:有没有什么方法可以从Ruby向Perl程序提供输入(除了参数)?Ruby和Perl之间没有桥梁,我错了吗?在导航提示时与程序的标准输入交互似乎是错误的方式,我正在处理的程序设计良好,并且具有包含适当Perl函数的库。 最佳答案 有Inline::Ruby模

  4. ruby - IRb:如何使用预加载类启动交互式 ruby​​ session - 2

    在我采用Ruby语言的过程中,我花了很多时间在IRb中。太棒了!但是,由于我不是很清楚它的功能,并且对Ruby仍然是个“笨蛋”,所以我想知道以下内容:如何在不重新启动IRb的情况下“刷新”session(或者这是不可能的)。如何配置IRb加载一堆源文件“hello.rb”和“hello_objects.rb”,即在启动时?我在这些方面投入了大量工作,如果知道加载这些类的速记,而无需再次为每个类手动键入“加载”,那就太好了。 最佳答案 我不确定是否可以“刷新”session。但是,您可以像这样加载您的类:irb-r'hello.rb'

  5. ruby - Lisp - 是否适合网络编程/应用程序(交互式)? ruby 的方式是? php的方式是? - 2

    Lisp是否适合Web编程/应用程序(交互式),就像ruby​​和php一样?需要考虑的事情是:易于使用可部署性难度(尤其是对于编程初学者而言)(编辑)在阅读PaulGraham'sessay之后,我特别提到了CommonLisp.将是我的第一门编程语言。在这方面。这样做合适吗?我听说Clojure的宏功能不如CommonLisp的强大,这就是我尝试学习Clojure的原因。它教授编程并且非常强大。 最佳答案 Lisp是一个语系,而不是单一的语言。为了稍微回答您的问题,是的,存在用于各种Lisp方言的Web框架,例如用于Common

  6. BigData/Cloud Computing:基于阿里云技术产品的人工智能与大数据/云计算/分布式引擎的综合应用案例目录来理解技术交互流程 - 2

    BigData/CloudComputing:基于阿里云技术产品的人工智能与大数据/云计算/分布式引擎的综合应用案例目录来理解技术交互流程目录一、云计算网站建设:部署与发布网站建设:简单动态网站搭建云服务器管理维护云数据库管理与数据迁移云存储:对象存储管理与安全超大流量网站的负载均衡二、大数据MOOC网站日志分析搭建企业级数据分析平台基于LBS的热点店铺搜索基于机器学习PAI实现精细化营销基于机器学习的客户流失预警分析使用DataV制作实时销售数据可视化大屏使用MaxCompute进行数据质量核查使用Quick BI制作图形化报表使用时间序列分解模型预测商品销量三、云安全云平台使用安全云上服务

  7. ruby-on-rails - 如何从 Ruby 与 CalDAV 服务器交互? - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。关闭4年前。Improvethisquestion我需要在Ruby(准确地说是Rails)应用程序中使用CalDAV在日历服务器上创建事件。我看过一些不同的图书馆并进行了一些谷歌搜索。我查看了ri-cal(http://ri-cal.rubyforge.org/rdoc/),但不确定它是否支持将数据发送到服务器,或者我是否必须自己这样做,这看起来很有希望http://www.local-guru.net/blog/p

  8. ruby - 在 ruby​​ 中运行系统命令并与之交互 - 2

    我需要在命令行上运行一个命令来请求用户响应。如果它有帮助,命令是:gpg--recipient"SomeName"--encrypt~/some_file.txt当你运行它时,它会发出警告然后询问:Usethiskeyanyway?(y/N)响应“y”让它正确完成。我一直在尝试使用open4gem但我无法让它正确指定“y”。这是我尝试过的:Open4::popen4(cmd)do|pid,stdin,stdout,stderr|stdin.puts"y"stdin.closeputs"pid:#{pid}"puts"stdout:#{stdout.read.strip}"puts"st

  9. ruby - 我将如何以编程方式与 VST(i) 插件交互以合成音频? - 2

    以VSTiTriforce为例,由Tweakbench提供。当加载到市场上的任何VST主机时,它允许主机向VSTi发送(大概是MIDI)信号。然后VSTi将处理该信号并输出​​由VSTi内的软件乐器创建的合成音频。例如,将A4(我相信是MIDI音符)发送到VSTi会导致它合成高于中央C的A。它将音频数据发送回VST主机,然后它可以在我的扬声器上播放或将其保存为.wav或其他一些音频文件格式。假设我有Triforce,我正在尝试用我选择的语言编写一个程序,它可以通过发送要合成的A4纸条与VSTi交互,并自动将其保存到系统上的文件?最终,我希望能够解析整个单轨MIDI文件(使用已经可用于此

  10. 微信小程序 webview页面分享弹窗 - 2

    微信小程序webview中使用cover-view展示分享弹窗公司业务需要在webview中添加分享弹窗,可以发送给朋友及生成海报分享,因为好几个详情都需要这个功能,因此抽离了share-sheet的组件,ui效果如下图:点击分享,显示以下弹窗share-sheet.wxml如下:{!show}}"catchtouchmove="poptouchmove">发送给朋友生成海报取消share-sheet.js如下:Component({options:{addGlobalClass:true,},/***组件的属性列表*/properties:{//控制弹窗显示与否show:{type:Bool

随机推荐