Flutter加载Html并实现与JS 的双向调用

https://blog.csdn.net/zl18603543572/article/details/96585707

2019-07-20 16:01:01 早起的年轻人 阅读数 2025  收藏 更多

分类专栏: flutter  flutter 从入门 到精通 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 

本文链接:https://blog.csdn.net/zl18603543572/article/details/96585707

更多文章请查看 flutter从入门 到精通

可以用来加载 Html 页面,以实现 Android 中 WebView 或者 是 iOS 中的 UIWebView 中的功能。

Flutter中可用于来加载 Html 页面的插件 ,

flutter_WebView_plugin

webView_flutter

flutter_inappbrowser

html

flutter_html

flutter_html_view

这些多多少满足不了我项目中的需求,所以花了几天时间开发了 Flutter_Fai_Webview 插件,可实现 Android 中 WebView 或者 是 iOS 中的 UIWebView 中的功能,因为 Flutter_Fai_Webview 插件本质上是通过 PlatformView 功能将原生的 View 嵌套在 Flutter 中。

插件源码在这里

开发插件要具备的知识:

Flutter 与 原生 Android iOS 双向通信

Flutter通过MethodChannel实现Flutter 与Android iOS 的双向通信

Flutter通过BasicMessageChannel实现Flutter 与Android iOS 的双向通信

Flutter 中内嵌 Android iOS 原生View的编程基础

flutter调用android 原生TextView 

flutter调用ios 原生View 

最重要的一点是 具备 Android iOS 原生语言的开发能力

Flutter_Fai_Webview 插件可实现的功能:

同时适配于 Android Ios 两个平台

通过 url 来加载渲染一个Html 页面

加载 Html 文本数据 如 <html> .... </html>等

加载 Html 标签数据 如  <p> ... </p>

实现 WebView 加载完成后,自动测量 WebView 的高度,并回调 Flutter

实现 WebView 加载完成监听

实现 WebView 上下滑动、滑动到顶部兼听、滑动到底部兼听并回调 Flutter

实现 兼听 WebView 输出日志并将日志回调 Flutter

实现 为 Html 页面中所有的图片添加点击事件 并回调 Flutter

实现 Html 中Js 调用 Flutter 页面功能

实现 Flutter 页面中 触发 Html 页面中 Js 方法

本插件开发的过程将在这里详细论述

也就是说在这里将教会你 开发一个 Flutter 插件。

Flutter 加载 HTML 详细阐述(iOS 端实现)

Flutter 加载 HTML 详细阐述(Android 端实现)

开始使用

1 基本使用说明

1.1 Flutter 项目中 pubspec.xml 文件中 配置插件

  flutter_fai_webview:

    git:

      url: https://github.com/zhaolongs/Flutter_Fai_Webview.git

      ref: master

1.2 在使用到 WebView 页面中

引入头文件

import 'package:flutter_fai_webview/flutter_fai_webview.dart';

1

1.3 创建 WebView 组件

    //使用插件 FaiWebViewWidget

    webViewWidget = FaiWebViewWidget(

      //webview 加载网页链接

      url: htmlUrl,

      //webview 加载信息回调

      callback: callBack,

      //输出日志

      isLog: true,

    );

1.4 FaiWebViewWidget 构造参数说明

  FaiWebViewWidget({

    //webview 加载网页链接

    this.url,

    //webview 加载 完整的 html 文件数据  如 <html> .... </html>

    // 不完整的 html 文件数据 如 <p></p> 配置到此项,用此属性来加载,只会渲染 <p> ... </p> 中已有的样式 不会适配移动端显示

    this.htmlData,

    //webview 加载完整的 html 文件数据 或者是 不完整的 html 文件数据 如 <p></p>

    //不完整的 html 文件数据 如 <p></p> 配置到此项,会自动将不完整的 html 文件数据 添加 <html><head> .. </head> <body> 原来的内容 </body></html>,并适配移动端

    this.htmlBlockData,

    //输出 Log 日志功能

    this.isLog,

    // 为 Html 页面中所有的图片添加 点击事件 并通过回调 通知 Flutter 页面

    // 只有使用 htmlBlockData 属性加载的页面才会有此效果

    this.htmlImageIsClick = false,

    // Html 页面中图片点击回调

    this.imageCallBack,

    // Html 页面中所有的消息回调

    this.callback,

  });


1.5 原生回调 Flutter 的 callback 以及 Html 页面中 图片 点击回调说明

  /**

  * code 原生 Android iOS 回调 Flutter 的消息类型标识

  * message 消息类型日志

  * content 回调的基本数据

  */

  Function(int code, String message, dynamic content) callback;


详细说明

  //当前点击的图片 URL

  String imageUrl = null;

  //是否显示浮动按钮

  bool isShowFloat = false;

  /**

  * code 当前点击图片的 位置

  * url 当前点击图片对应的 链接

  * images 当前 Html 页面中所有的图片集合

  */

  void imageCallBack(int code, String url, List<String> images) {

    imageUrl = url;

    setState(() {});

  }

  void callBack(int code, String msg, content) {

    String call = "回调 code:" +

        code.toString() +

        " msg:" +

        msg.toString() +

        " content:" +

        content.toString();

    if (code == 201) {

      //加载页面完成后 对页面重新测量的回调

      //这里没有使用到

      //当FaiWebViewWidget 被嵌套在可滑动的 widget 中,必须设置 FaiWebViewWidget 的高度

      //设置 FaiWebViewWidget 的高度 可通过在 FaiWebViewWidget 嵌套一层 Container 或者 SizeBox

      webViewHeight = content;

    } else if (code == 202) {

      // Html 页面中 Js 的回调

      // Html 页面中的开发需要使用 Js 调用  【 Android 中 使用 controll.otherJsMethodCall( json )】 【iOS中 直接调用 otherJsMethodCall( json ) 】

      // 在 Flutter 中解析 json 然后加载不同的功能

      String jsJson = content;

    } else if (code == 203) {

      // 为 Html 页面中的图片添加 点击事件后,点击图片会回调此方法

      // content 为当前点击图片的 地址

      // 实现更多功能 比如 一个 Html 页面中 有5张图片,点击放大查看并可右右滑动

      // 这个功能可以在 imageCallBack 回调中处理

    } else if (code == 301) {

      //当 WebView 滑动到顶部的回调

    } else if (code == 302) {

      //当 WebView 开始向下滑动时的回调

      //隐藏按钮

      isShowFloat = true;

    } else if (code == 303) {

      //当 WebView 开始向上滑动时的回调

      //显示按钮

      isShowFloat = false;

    } else if (code == 304) {

      //当 WebView 滑动到底部的回调

    } else if (code == 401) {

      //当 WebView 开始加载的回调

    } else if (code == 402) {

      //当 WebView 加载完成的回调

    } else if (code == 403) {

      // WebView 中 Html中日志输出回调

    } else if (code == 401) {

      // WebView 加载 Html 页面出错的回调

    } else if (code == 501) {

      // 当 Html 页面中有 Alert 弹框弹出时 回调消息

    } else if (code == 1000) {

      // 操作失败 例如 空指针异常 等等

    } else {

      //其他回调

    }

    setState(() {

      message = call;

    });

  }

1.6 Flutter 刷新页面

//调用此方法 便可刷新(重新加载页面)

webViewWidget.refresh();

1.7 Flutter 调用 JS 方法

//testAlert() 就是我们要调用的 Html 页面中 JS的方法

// testAlert() 可以自定义与 Html 中的 JS 开发约定

webViewWidget.loadJsMethod("testAlert()");

2 Flutter 加载页面

2.1 通过 url 加载 Html 页面

import 'package:flutter/material.dart';

import 'package:flutter_fai_webview/flutter_fai_webview.dart';

/**

*  加载地址

*  通过 url 加载了一个 Html页面 是取常用的方法

*/

class DefaultLoadingWebViewUrlPage extends StatefulWidget {

  @override

  MaxUrlState createState() => MaxUrlState();

}

class MaxUrlState extends State<DefaultLoadingWebViewUrlPage> {

  //要显示的页面内容

  Widget childWidget;

  //加载Html的View

  FaiWebViewWidget webViewWidget;

  //原生 发送给 Flutter 的消息

  String message = "--";

  // 页面

  String htmlUrl = "https://blog.csdn.net/zl18603543572";

  @override

  void initState() {

    super.initState();

    //使用插件 FaiWebViewWidget

    webViewWidget = FaiWebViewWidget(

      //webview 加载网页链接

      url: htmlUrl,

      //webview 加载信息回调

      callback: callBack,

      //输出日志

      isLog: true,

    );

  }

  @override

  Widget build(BuildContext context) {

    return Scaffold(

        appBar: AppBar(

          leading: IconButton(

            onPressed: () {

              Navigator.pop(context);

            },

            icon: Icon(Icons.arrow_back_ios),

          ),

          title: Container(

            padding: EdgeInsets.only(left: 10, right: 10),

            height: 28,

            alignment: Alignment(0, 0),

            color: Color.fromARGB(90, 0, 0, 0),

            child: Text(

              message,

              style: TextStyle(color: Colors.white, fontSize: 12),

            ),

          ),

        ),

        body: webViewWidget,

      );

  }

  void callBack(int code, String msg, content) {

    //加载页面完成后 对页面重新测量的回调

    //这里没有使用到

    //当FaiWebViewWidget 被嵌套在可滑动的 widget 中,必须设置 FaiWebViewWidget 的高度

    //设置 FaiWebViewWidget 的高度 可通过在 FaiWebViewWidget 嵌套一层 Container 或者 SizeBox

    if (code == 201) {

      //页面加载完成后 测量的 WebView 高度

      int webViewHeight = content;

      print("webViewHeight " + webViewHeight.toString());

    } else {

      //其他回调

    }

    setState(() {

      message = "回调:code[" + code.toString() + "]; msg[" + msg.toString() + "]";

    });

  }

}

2.2 通过 Html Data 加载 Html 页面

import 'package:flutter/material.dart';

import 'package:flutter_fai_webview/flutter_fai_webview.dart';

/**

*  通过 htmlBlockData 加载 Html 数据 并添加移动适配

*/

class DefaultHtmlBlockDataPage2 extends StatefulWidget {

  @override

  DefaultHtmlBlockDataPageState createState() =>

      DefaultHtmlBlockDataPageState();

}

class DefaultHtmlBlockDataPageState extends State<DefaultHtmlBlockDataPage2> {

  FaiWebViewWidget webViewWidget;

  //原生 发送给 Flutter 的消息

  String message = "--";

  double webViewHeight = 100;

  //要显示的页面内容

  Widget childWidget;

  String htmlBlockData = "<!DOCTYPE html><html> <head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1,maximum-scale=1\"> </head> <body><p><br/></p><p>生物真题&nbsp;</p><p><img src=\"http://pic.studyyoun.com/1543767087584\" title=\"\" alt=\"\"/></p><p><img src=\"http://pic.studyyoun.com/1543767100547\" title=\"\" alt=\"\"/></p><p><br/></p><p><br/></p><p><br/></p></body></html>";

  @override

  void initState() {

    super.initState();

    //使用插件 FaiWebViewWidget

    webViewWidget = FaiWebViewWidget(

      //webview 加载网页链接

      htmlBlockData: htmlBlockData,

      //webview 加载信息回调

      callback: callBack,

      //输出日志

      isLog: true,

    );

  }

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        leading: IconButton(

          onPressed: () {

            Navigator.pop(context);

          },

          icon: Icon(Icons.arrow_back_ios),

        ),

        title: Container(

          padding: EdgeInsets.only(left: 10, right: 10),

          height: 28,

          alignment: Alignment(0, 0),

          color: Color.fromARGB(90, 0, 0, 0),

          child: Text(

            message,

            style: TextStyle(color: Colors.white, fontSize: 12),

          ),

        ),

      ),

      body: Container(child: webViewWidget,),

    );

  }

  callBack(int code, String msg, content) {

    //加载页面完成后 对页面重新测量的回调

    //这里没有使用到

    //当FaiWebViewWidget 被嵌套在可滑动的 widget 中,必须设置 FaiWebViewWidget 的高度

    //设置 FaiWebViewWidget 的高度 可通过在 FaiWebViewWidget 嵌套一层 Container 或者 SizeBox

    if (code == 201) {

      webViewHeight = content;

      print("webViewHeight " + webViewHeight.toString());

    } else {

      //其他回调

    }

    setState(() {

      message = "回调:code[" + code.toString() + "]; msg[" + msg.toString() + "]";

    });

  }

}

2.3 加载混合页面

也就是说 一个页面中,一部分是 Flutter Widget 一部分是 webview 加载。

import 'package:flutter/material.dart';

import 'dart:async';

import 'package:flutter_fai_webview/flutter_fai_webview.dart';

/**

*  混合页面加载

*

*/

class DefaultHexRefreshPage extends StatefulWidget {

  @override

  MaxUrlHexRefreshState createState() => MaxUrlHexRefreshState();

}

class MaxUrlHexRefreshState extends State<DefaultHexRefreshPage> {

  FaiWebViewWidget webViewWidget;

  //原生 发送给 Flutter 的消息

  String message = "--";

  String htmlUrl = "https://blog.csdn.net/zl18603543572";

  double webViewHeight = 1;

  @override

  void initState() {

    super.initState();

    //使用插件 FaiWebViewWidget

    webViewWidget = FaiWebViewWidget(

      //webview 加载网页链接

      url: htmlUrl,

      //webview 加载信息回调

      callback: callBack,

      //输出日志

      isLog: true,

    );

  }

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        leading: IconButton(

          onPressed: () {

            Navigator.pop(context);

          },

          icon: Icon(Icons.arrow_back_ios),

        ),

        title: Container(

          padding: EdgeInsets.only(left: 10, right: 10),

          height: 28,

          alignment: Alignment(0, 0),

          color: Color.fromARGB(90, 0, 0, 0),

          child: Text(

            message,

            style: TextStyle(color: Colors.white, fontSize: 12),

          ),

        ),

      ),

      body: buildRefreshHexWidget(),

    );

  }

  /**

  * 需要注意的是

  * RefreshIndicator 会覆盖 WebView 的滑动事件

  * 所有关于 监听 WebView 的滑动监听将会失效

  */

  Widget buildRefreshHexWidget() {


    return RefreshIndicator(

      //下拉刷新触发方法

      onRefresh: _onRefresh,

      //设置webViewWidget

      child: SingleChildScrollView(

        child: Column(

          children: <Widget>[

            Container(

              color: Colors.grey,

              height: 220.0,

              child: Column(mainAxisSize: MainAxisSize.min,children: <Widget>[

                  Center(child: Text("这里是 Flutter widget  "),)

              ],),

            ),

            Align(

              alignment: Alignment(0, 0),

              child: Text("以下是 Html 页面 "),

            ),

            Container(

              color: Colors.redAccent,

              height: 1.0,

            ),

            Container(

              height: webViewHeight,

              child: webViewWidget,

            )

          ],

        ),

      ),

    );

  }

  Future<Null> _onRefresh() async {

    return await Future.delayed(Duration(seconds: 1), () {

      print('refresh');

      webViewWidget.refresh();

    });

  }

  callBack(int code, String msg, content) {

    //加载页面完成后 对页面重新测量的回调

    if (code == 201) {

      //更新高度

      webViewHeight = content;

      print("webViewHeight " + content.toString());

    } else {

      //其他回调

    }

    setState(() {

      message = "回调:code[" + code.toString() + "]; msg[" + msg.toString() + "]";

    });

  }

}

文章最后发布于: 2019-07-20 16:01:01

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容

  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,383评论 0 17
  • WebView·开车指南 2016-08-31BugDev 北京市东城区首席Bug布道师开山之作,一整月交通事故血...
    53c021c38a1d阅读 827评论 0 1
  • WebView简介 String getUrl():获取当前页面的URL。 reload():重新reload当前...
    QM阅读 3,096评论 0 52
  • Tips 由于WebView的用法实在太多,如果您只是想查询某个功能的使用——建议Ctrl+F(Commad+F)...
    BugDev阅读 7,743评论 11 109
  • 幽默感帮助家长和孩子,轻松一下,记住欢笑乐趣。今天可以尝试一下,在大棒和二棒闹别扭时,轻轻的把它们叠在一起说叠猪猪...
    棒棒妈_0489阅读 62评论 0 0