当前位置:   article > 正文

基于webview_flutter实现JsBridge的简单封装_platformexception(unregistered_view_type, a uikitv

platformexception(unregistered_view_type, a uikitview widget is trying to cr

人气很高的flutter_webview_plugin,在打开多个WebView时会出错,而且缺少2个重要的功能:

  • 不能在JS中调用Flutter方法
  • 不能在H5进入某个URL之前拦截

虽然该插件不够完整,但是使用起来很方便,封装了很多功能,如果交互不多可以用该插件。

本文基于flutter官方webview_flutter实现JsBridge的简单封装,实现在H5页面点击按钮调起flutter弹窗,具体步骤如下:

环境搭建

1. Depend on it

Add this to your package’s pubspec.yaml file:

dependencies:
webview_flutter: ^0.3.22+1
  • 1
  • 2
2. Install it

You can install packages from the command line with Flutter:

$ flutter pub get
  • 1

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:

import 'package:webview_flutter/webview_flutter.dart';
  • 1
4. For iOS,you need to add ios-Runner-Info.plist with
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>
  • 1
  • 2

否则报错:

[VERBOSE-2:shell.cc(207)] Dart Error: Unhandled exception:
PlatformException(unregistered_view_type, trying to create a view with an unregistered type, unregistered view type: ‘plugins.flutter.io/webview’)
#0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:569:7)
#1 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:321:33)

#2 PlatformViewsService.initUiKitView (package:flutter/src/services/platform_views.dart:168:41)
#3 _UiKitViewState._createNewUiKitView (package:flutter/src/widgets/platform_view.dart:621:71)
#4 _UiKitViewState._initializeOnce (package:flutter/src/widgets/platform_view.dart:571:5)
#5 _UiKitViewState.didChangeDependencies (package:flutter/src/widgets/platform_view.dart:581:5)
#6 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4380:12)
#7 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4208:5)
#8 Element.inf<…>

代码实现

main.dart

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import './JsBridgeUtil.dart';

const String kNavigationExamplePage = '''
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Navigation Delegate Example</title>
        <style type="text/css">
            .click-button {
                width: 300px;
                height: 100px;
                font-size: 40px;
            }
        </style>
    </head>
    <body>
    <p style="color: #161823; font-size: 40px">
        The test of method execution from H5 to Flutter
    </p>
    <Button class="click-button" onclick="executeMethod()">点我</Button>
    </body>
    <script defer type="text/javascript">
        function executeMethod() {
            let paramObject = {
                method: 'showToast',
                params: {
                    message: 'User Agent: ' + navigator.userAgent
                }
            }
            ViaBridge.postMessage(JSON.stringify(paramObject));
        }
    </script>
</html>
''';

void main() => runApp(MaterialApp(home: WebViewExample()));

class WebViewExample extends StatefulWidget {
  @override
  _WebViewExampleState createState() => _WebViewExampleState();
}

class _WebViewExampleState extends State<WebViewExample> {
  // webview 加载进度设置
  final Completer<WebViewController> _controller =
      Completer<WebViewController>();
  final String contentBase64 =
  base64Encode(const Utf8Encoder().convert(kNavigationExamplePage));

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter WebView example'),
      ),

    body: Builder(builder: (BuildContext context) {
      return WebView(
          // 要显示的url
          initialUrl: 'data:text/html;base64,$contentBase64',
          // JS执行模式 默认是disabled
          javascriptMode: JavascriptMode.unrestricted,
          onWebViewCreated: (WebViewController webViewController) {
            // 在WebView创建完成后会产生一个 webViewController
            _controller.complete(webViewController);
          },
          // 使用javascriptChannel JS可以调用Flutter
          // javascriptChannels参数接受Set<JavascriptChannel>一个成员类型为JavascriptChannel的Set集合
          javascriptChannels: <JavascriptChannel>[
            _toasterJavascriptChannel(context),
          ].toSet(),
          // 拦截请求
          navigationDelegate: (NavigationRequest request) {
            if (request.url.startsWith('https://www.youtube.com/')) {
              print('blocking navigation to $request}');
              return NavigationDecision.prevent;
            }
            print('allowing navigation to $request');
            return NavigationDecision.navigate;
          },
          onPageStarted: (String url) {
            print('Page started loading: $url');
          },
          onPageFinished: (String url) {
            print('Page finished loading: $url');
          },
          gestureNavigationEnabled: true,
        );
      }),
    );
  }

    JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
    return JavascriptChannel(
      name: 'ViaBridge',
      onMessageReceived: (JavascriptMessage message) async{
        String jsonStr = message.message;
        JsBridgeUtil.executeMethod(context, JsBridgeUtil.parseJson(jsonStr));
      });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106

JsBridgeUtil.dart

import 'dart:convert';
import './JsBridge.dart';
import 'package:flutter/material.dart';

/*
 *  JsBridge执行类
**/ 

class JsBridgeUtil {
  /// 将json字符串转化成对象
  static JsBridge parseJson(String jsonStr) {
    JsBridge jsBridgeModel = JsBridge.fromMap(jsonDecode(jsonStr));
    return jsBridgeModel;
  }

  /// 向H5暴露接口调用
  static executeMethod(BuildContext context, JsBridge jsBridge) async{
    if (jsBridge.method == 'showToast') {
        Scaffold.of(context).showSnackBar(
          SnackBar(content: Text(jsBridge.params['message'])),
        );
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

JsBridge.dart

/*
 *  定义JsBridge的基本结构
**/ 
class JsBridge {
  String method; // 方法名
  Map params; // 传递数据
  Function callback; // 执行回调

  JsBridge(this.method, this.params, this.callback);

  // jsonEncode方法中会调用实体类的这个方法。如果实体类中没有这个方法,会报错。
  Map toJson() {
    Map map = new Map();
    map["method"] = this.method;
    map["params"] = this.params;
    map["callback"] = this.callback;
    return map;
  }
  // jsonDecode(jsonStr)方法返回的是Map<String, dynamic>类型,需要这里将map转换成实体类
  static JsBridge fromMap(Map<String, dynamic> map) {
    JsBridge jsBridgeModel =  new JsBridge(map['method'], map['params'], map['callback']);
    return jsBridgeModel;
  }

  @override
  String toString() {
    return "JsBridge: {method: $method, params: $params, callback: $callback}";
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

在H5页面点击按钮“点我“就可以调起flutter弹窗,展示navigator.userAgent信息

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/93507
推荐阅读
相关标签
  

闽ICP备14008679号