Flutter网页视图展示插件flutter_webview_kit的使用

Flutter网页视图展示插件flutter_webview_kit的使用

一、JS

二、JS注入方法

可以实现在原有网页上增加按钮,点击按钮后执行JS代码。

// 使用方法1: 在网页上添加按钮
controller.injectedJs()

// 使用方法2: 在原生上添加按钮
AddJSChannel_InjectedJS.testJSInjectedButton
AddJSChannel_InjectedJS.testJSRunButton()

方法1的示例:

Future<bool> injectedJs_demo() async {
  String addPosition = 'top';
  String execJSMethod = "testInjectedMethod_showJsonWithCallbackMethod";
  Map<String, dynamic> execJSParams = {
    // "callbackMethod": "alert",
    "callbackMethod": "h5CallBridgeAction_showAppToast",
    "message": "这是测试运行注入的js",
    "dialogSubjectId": "1234567890",
    "dialogType": "user",
  };

  return injectedJs(
    execJSMethod: execJSMethod,
    execJSParams: execJSParams,
    execJSCallBackMapGetHandle: () {
      var sendMessage = {
        "h5Title": "这是h5内部返回的标题1",
        "h5Message": "这是h5内部返回的描述信息2",
        "message": "这是h5内部返回的描述信息message3",
      };
      return sendMessage;
    },
    injectedUIPosition: addPosition,
  );
}

方法2的示例:

Container(
  color: Colors.amber,
  height: 2 * 40 + 10,
  child: Column(
    mainAxisAlignment: MainAxisAlignment.spaceAround,
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      AddJSChannel_InjectedJS.testJSInjectedButton(
        execJSMethod: execJSMethod,
        webViewControllerGetBlock: () => _controller,
      ),
      AddJSChannel_InjectedJS.testJSRunButton(
        execJSMethod: execJSMethod,
        execJSParams: execJSParams,
        hasInjectedJS: hasInjectedJS,
        webViewControllerGetBlock: () => _controller,
      ),
    ],
  ),
)

三、原理

1、注入点击按钮

1、创建要注入的元素/区域

String injectedUIHtml = """
          // 创建一个新的按钮元素
          var newButton = document.createElement("button");
          // 设置按钮的样式
          newButton.style.height = '200px';
          newButton.style.width = '320px'; // 可以根据需要设置宽度
          newButton.style.backgroundColor = '#4CAF50'; // 设置背景颜色
          newButton.style.color = 'white'; // 设置文字颜色
          newButton.style.border = 'none'; // 去除边框
          newButton.style.cursor = 'pointer'; // 鼠标悬停时显示指针 
          // 设置按钮的文本
          newButton.textContent = '点击我';
      """;

设置按钮文本和点击事件

详见:下文【2、注入按钮要执行的js方法】中的【将注入的事件绑定到注入的按钮的点击事件上】

2、注入位置 top(默认) \ bottom \ overlay

injectedUIHtml += """
					if (`\${myContentPosition}` == 'bottom') {
            // 将按钮添加到body中,可以根据需要添加到其他位置
            document.body.appendChild(newButton);
          } else if (`\${myContentPosition}` == 'overlay') {
            newButton.style.position = 'fixed'; // 固定位置,不随页面滚动
            newButton.style.top = '0'; // 定位到页面顶部
            newButton.style.left = '0'; // 定位到页面左侧
            newButton.style.zIndex = '1000'; // 确保按钮在页面最上层
            newButton.innerHTML = '<div style="padding: 20px; text-align: center;">点击我啊</div>';
            document.body.insertAdjacentElement('afterbegin', newButton);
          } else {
            // 将按钮添加到body的第一个子元素,使其成为页面内容的顶部
            document.body.insertBefore(newButton, document.body.firstChild);
          }
          """;

3、注入时机 onload / now

injectedUIHtml += """
		if (injectedTime == 'onload') {
      injectedUIHtml = """
        window.addEventListener('load', function() {
          $injectedUIHtml
        });
      """;
    }
    """;

    return runJavaScript(injectedUIHtml);

如此,页面上就有一个注入进去的按钮。

2、注入按钮要执行的js方法

1、注入按钮要执行的js方法及让js调用app方法的核心如下:

"""
		window[`\${injectedJSMethod}`] = function(json) {
      console.log(`正在执行:\${injectedJSMethod}`)
      ......xxx......

      var funName = callbackMethod;
      var sendMessage = sendMessageString;
      try {
        eval(funName).postMessage(sendMessage);
      } catch (err) {
        var evalErrorMessage = `【执行错误如下】\n方法:\${funName} \n原因:\${err}`;
        console.log(evalErrorMessage);
        alert(evalErrorMessage);
      }
    };
  """;

完整如下:

Future<bool> _injectedJavaScript({
    required String jsMethod,
    Map<String, dynamic> Function()? execJSCallBackMapGetHandle,
  }) async {
    String? execJSCallBackJson;
    if (execJSCallBackMapGetHandle != null) {
      Map<String, dynamic> execJSCallBackMap = execJSCallBackMapGetHandle();
      execJSCallBackJson = json.encode(execJSCallBackMap);
    }

    String addJavaScript = """
    // 测试方法带回调
    // 方法一:
    // window.testInjectedMethod_showJsonWithCallbackMethod = function(json) {
    // 方法二:使用变量。📢:注意js中使用 外部变量 和 使用内部变量 的写法区别
    // var injectedJSMethod = "testInjectedMethod_showJsonWithCallbackMethod"
    var injectedJSMethod = `$jsMethod`;
    window[`\${injectedJSMethod}`] = function(json) {
      console.log(`正在执行:\${injectedJSMethod}`)
      var arguments = JSON.parse(json);
      var callbackMethod = arguments['callbackMethod'];
      if (callbackMethod === undefined || callbackMethod === null) {
        var errorMessage = "缺少 callbackMethod 参数";
        alert(errorMessage);
        return;
      }
      delete arguments.callbackMethod; // 删除键为 'callbackMethod' 的属性

      var execMessage = `正在执行app调用h5,并返回回调:`
      execMessage += `\n执行js方法:\${injectedJSMethod}`
      execMessage += `\n执行js参数:\${JSON.stringify(arguments)}`
      execMessage += `\n回调方法:\${callbackMethod}`
      var sendMessageString = `$execJSCallBackJson`;
      if (sendMessageString === undefined || sendMessageString === null) {
        sendMessageString = "";
      }
      execMessage += `\n回调值:\${sendMessageString}`
      console.log(execMessage)
      alert(execMessage);
      var funName = callbackMethod;
      var sendMessage = sendMessageString;
      try {
        eval(funName).postMessage(sendMessage);
      } catch (err) {
        var evalErrorMessage = `【执行错误如下】\n方法:\${funName} \n原因:\${err}`;
        console.log(evalErrorMessage);
        alert(evalErrorMessage);
      }
    };
  """;

    await runJavaScript(addJavaScript);

    return true;
  }

3、将注入的事件绑定到注入的按钮的点击事件上,设置按钮文本和点击事件

injectedUIHtml += """
					var jsMethod2 = `$execJSMethod`
          newButton.textContent = `点击我\n执行\${jsMethod2}`;
          // 添加点击事件处理器
          newButton.onclick = function() {
            // 原始代码
            // window.testInjectedMethod_showJsonWithCallbackMethod(JSON.stringify({
            //   "callbackMethod": "h5CallBridgeAction_showAppToast",
            //   "message": "这是测试运行注入的js",
            // }));

            // 外部变量代码
            var jsMethod = `$execJSMethod`
            var sendMessageString = `$execJSJsonParams`; // 外部的map无法传到内部,需要转为string
            // alert(`按钮被点击了!\${sendMessageString}`);
            window[`\${jsMethod}`](sendMessageString);
          };
          """;

更多关于Flutter网页视图展示插件flutter_webview_kit的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter网页视图展示插件flutter_webview_kit的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


flutter_webview_kit 是一个用于在 Flutter 应用中嵌入网页视图的插件。它允许你在应用中加载和显示网页内容,并且可以自定义网页视图的行为和样式。以下是如何使用 flutter_webview_kit 插件的基本步骤。

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 flutter_webview_kit 插件的依赖。

dependencies:
  flutter:
    sdk: flutter
  flutter_webview_kit: ^latest_version

然后运行 flutter pub get 来安装依赖。

2. 导入插件

在你的 Dart 文件中导入 flutter_webview_kit 插件。

import 'package:flutter_webview_kit/flutter_webview_kit.dart';

3. 使用 WebView

你可以在你的 Flutter 应用中使用 WebView 组件来加载和显示网页内容。

import 'package:flutter/material.dart';
import 'package:flutter_webview_kit/flutter_webview_kit.dart';

class MyWebView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter WebView Kit Example'),
      ),
      body: WebView(
        initialUrl: 'https://flutter.dev',
        javascriptMode: JavascriptMode.unrestricted,
      ),
    );
  }
}

void main() {
  runApp(MaterialApp(
    home: MyWebView(),
  ));
}

4. 配置 WebView

WebView 组件提供了多种配置选项,你可以根据需要进行设置。

  • initialUrl: 初始加载的 URL。
  • javascriptMode: 是否启用 JavaScript,可以是 JavascriptMode.disabledJavascriptMode.unrestricted
  • onPageStarted: 当网页开始加载时触发的回调。
  • onPageFinished: 当网页加载完成时触发的回调。
  • onWebResourceError: 当网页资源加载失败时触发的回调。

例如:

WebView(
  initialUrl: 'https://flutter.dev',
  javascriptMode: JavascriptMode.unrestricted,
  onPageStarted: (String url) {
    print('Page started loading: $url');
  },
  onPageFinished: (String url) {
    print('Page finished loading: $url');
  },
  onWebResourceError: (WebResourceError error) {
    print('Error loading page: ${error.description}');
  },
);

5. 处理导航

你还可以通过 navigationDelegate 来处理网页中的导航请求。

WebView(
  initialUrl: 'https://flutter.dev',
  javascriptMode: JavascriptMode.unrestricted,
  navigationDelegate: (NavigationRequest request) {
    if (request.url.contains('flutter')) {
      return NavigationDecision.navigate;
    } else {
      return NavigationDecision.prevent;
    }
  },
);

6. 其他功能

flutter_webview_kit 还提供了其他一些高级功能,例如:

  • 注入 JavaScript:你可以通过 WebViewController 注入 JavaScript 代码。
  • 与 Flutter 通信:你可以通过 JavaScriptChannel 在网页和 Flutter 之间进行通信。

7. 示例代码

以下是一个完整的示例,展示了如何使用 flutter_webview_kit 插件。

import 'package:flutter/material.dart';
import 'package:flutter_webview_kit/flutter_webview_kit.dart';

class MyWebView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter WebView Kit Example'),
      ),
      body: WebView(
        initialUrl: 'https://flutter.dev',
        javascriptMode: JavascriptMode.unrestricted,
        onPageStarted: (String url) {
          print('Page started loading: $url');
        },
        onPageFinished: (String url) {
          print('Page finished loading: $url');
        },
        onWebResourceError: (WebResourceError error) {
          print('Error loading page: ${error.description}');
        },
        navigationDelegate: (NavigationRequest request) {
          if (request.url.contains('flutter')) {
            return NavigationDecision.navigate;
          } else {
            return NavigationDecision.prevent;
          }
        },
      ),
    );
  }
}

void main() {
  runApp(MaterialApp(
    home: MyWebView(),
  ));
}

8. 注意事项

  • iOS 配置:如果你在 iOS 上使用 flutter_webview_kit,请确保在 Info.plist 中添加以下配置以支持加载网页内容:
<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>
回到顶部