当前位置:   article > 正文

Flutter嵌入原生View,使用原生的WebView_flutter_inappwebview 支持安装原生接口

flutter_inappwebview 支持安装原生接口

在Flutter开发过程中,我们发现Flutter对于WebView的支持并不太好,而我们原生项目中,使用的腾讯的X5 WebView,所以也想在Flutter中进行嵌入使用。
具体的操作步骤如下

Android端

新建FlutterWebView,继承自PlatformView

class FlutterWebView(
    val context: Context,
    val messenger: BinaryMessenger,
    val viewId: Int,
    val args: Map<String, Any>?
) : PlatformView, MethodChannel.MethodCallHandler {
    private var webView: WebView? = null

    override fun getView(): View {
    	//curAct在Application.ActivityLifecycleCallbacks中进行注入
        val curAct = App.getApp().curActivity
        webView = WebView(curAct)
        //..... 这里可以进行webView的一些配置
        
        if (args != null) {
            val url = args["url"] as String
            webView!!.loadUrl(url)
        } else {
            throw IllegalArgumentException("args is null.")
        }
        return webView!!
    }

    override fun dispose() {
        webView?.destroy()
    }
}
  • 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

新建FlutterWebViewFactory,继承自PlatformViewFactory

class FlutterWebViewFactory(val messenger: BinaryMessenger) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {

    override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
        val flutterView = FlutterWebView(context, messenger, viewId, args as Map<String, Any>?)
        return flutterView
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

新建FlutterWebViewPlugin,继承自FlutterPlugin

class FlutterWebViewPlugin : FlutterPlugin {

    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        val messenger: BinaryMessenger = binding.binaryMessenger
        binding
            .platformViewRegistry
            .registerViewFactory(
                "plugins.flutter.test/webview", FlutterWebViewFactory(messenger)
            )
    }

    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {

    }

    companion object {
        @JvmStatic
        fun registerWith(registrar: PluginRegistry.Registrar) {
            registrar
                .platformViewRegistry()
                .registerViewFactory(
                    "plugins.flutter.test/webview",
                    FlutterWebViewFactory(registrar.messenger())
                )
        }
    }
}
  • 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

接着,我们需要注册这个FlutterWebViewPlugin
我们这里使用的是FlutterBoost框架,所以,直接在FlutterBoost初始化的地方进行注册即可

FlutterBoost.instance().setup(this, new FlutterBoostDelegate() {
        @Override
        public void pushNativeRoute(FlutterBoostRouteOptions options) {
            //...省略代码...
        }

        @Override
        public void pushFlutterRoute(FlutterBoostRouteOptions options) {
            //...省略代码...
        }
    }, engine -> {
    	//进行FlutterWebViewPlugin的注册
        engine.getPlugins().add(new FlutterWebViewPlugin());       
    });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

如果没有使用FlutterBoost,可以在继承自FlutterActivity的代理Activity中进行注册,具体可以看我的另一篇博客 Flutter和Native之间进行通信

Flutter端

新建liu_web_view.dart,相当于封装一个组件库

class LiuWebView extends StatefulWidget {
  final String url;
  final String iOSWebType;

  const LiuWebView(
      {Key? key,
      required this.url,
      this.iOSWebType = "full"})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return _LiuWebViewState();
  }
}

class _LiuWebViewState extends State<LiuWebView> {
  late MethodChannel webViewChannel;

  @override
  Widget build(BuildContext context) {
    if (defaultTargetPlatform == TargetPlatform.android) {
      return AndroidView(
          onPlatformViewCreated: (viewId) {
            initWebViewChannel(viewId);
          },
          creationParams: {'url': widget.url},
          creationParamsCodec: StandardMessageCodec(),
          viewType: 'plugins.flutter.test/webview');
    } /*else if (defaultTargetPlatform == TargetPlatform.iOS) {
      //IOS相关的逻辑我们可以先不去实现
      return UiKitView(
        onPlatformViewCreated: (viewId) {
          initWebViewChannel(viewId);
        },
        viewType: 'plugins.flutter.test/webview',
        layoutDirection: TextDirection.ltr,
        creationParams: <String, dynamic>{
          'url': widget.url,
          'type': widget.iOSWebType
        },
        creationParamsCodec: const StandardMessageCodec(),
      );
    }*/ else {
      throw Exception("not impl:" + defaultTargetPlatform.toString());
    }
  }
}
  • 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

最后,在需要的页面里进行使用即可

class _MyHomePageState extends State<MyHomePage> {
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Test"),
        ),
        body: Container(
          child: LiuWebView(url: "https://www.baidu.com"),
        ));
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Flutter和原生View进行双向的交互

如果我们需要将原生WebView的加载状态告知给Flutter,应该怎么做呢 ?
Flutter和原生的通信我们都知道,是使用Channel来进行交互的。同理,这里也是使用Channel,双向交互的话,我们可以选用MethodChannel。

Android端修改FlutterWebView

class FlutterWebView(
    val context: Context,
    val messenger: BinaryMessenger,
    val viewId: Int,
    val args: Map<String, Any>?
) : PlatformView, MethodChannel.MethodCallHandler {
    private var webView: WebView? = null
    private var methodChannel: MethodChannel

    init {
        Log.i(TAGs.FLUTTER_WEB_VIEW, "onPageStarted viewId:$viewId")
        methodChannel.setMethodCallHandler(this)
    }

    override fun getView(): View {
        val curAct = App.getApp().curActivity
        webView = WebView(curAct)

        webView?.setWebViewClient(object:WebViewClient(){
            override fun onPageStarted(
                webView: com.tencent.smtt.sdk.WebView?,
                url: String?,
                favicon: Bitmap?
            ) {
                super.onPageStarted(webView, url, favicon)
                //告知Flutter,WebView页面加载已经开始
                methodChannel.invokeMethod("onLoadStart", url)
            }

            override fun onPageFinished(webView: com.tencent.smtt.sdk.WebView?, url: String?) {
                super.onPageFinished(webView, url)
                //告知Flutter,WebView页面加载已经完毕
                methodChannel.invokeMethod("onLoadStop", url)
            }
        })

        if (args != null) {
            val url = args["url"] as String
            webView!!.loadUrl(url)
        } else {
            throw IllegalArgumentException("args is null.")
        }
        return webView!!
    }

    override fun dispose() {
        webView?.destroy()
    }

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
		//这里可以通过 Flutter传过来的 call.method 来实现 Flutter 对 原生的调用
		if(call.method == "xxxxx"){
			//do something
		}
    }
}
  • 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

Flutter端修改liu_web_view.dart

class LiuWebView extends StatefulWidget {
  final String url;
  final String iOSWebType;
  final LoadCallback? onLoadStart;
  final LoadCallback? onLoadStop;

  const LiuWebView(
      {Key? key,
      required this.url,
      this.iOSWebType = "full",
      this.onLoadStart,
      this.onLoadStop})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return _LiuWebViewState();
  }
}

typedef LoadCallback = void Function(String url);

class _LiuWebViewState extends State<LiuWebView> {
  late MethodChannel webViewChannel;

  void initWebViewChannel(int viewId) {
    webViewChannel = MethodChannel('com.test/webView$viewId');
    webViewChannel.setMethodCallHandler((call) {
      return handleCall(call);
    });
  }

  Future<dynamic> handleCall(MethodCall call) async {
    logDebug(TAGs.WEB_VIEW, "handleCall method:" + call.method);
    if (call.method == "onLoadStart") {
      //收到 原生WebView 开始加载的状态
      String url = call.arguments as String;
      widget.onLoadStart?.call(url);
    } else if (call.method == "onLoadStop") {
      //收到 原生WebView 结束加载的状态
      String url = call.arguments as String;
      widget.onLoadStop?.call(url);
    }
  }

  @override
  Widget build(BuildContext context) {
    if (defaultTargetPlatform == TargetPlatform.android) {
      return AndroidView(
          onPlatformViewCreated: (viewId) {
            initWebViewChannel(viewId);
          },
          creationParams: {'url': widget.url},
          creationParamsCodec: StandardMessageCodec(),
          viewType: 'plugins.flutter.test/webview');
    } /*
	IOS相关的逻辑我们可以先不去实现
	else if (defaultTargetPlatform == TargetPlatform.iOS) {
      return UiKitView(
        onPlatformViewCreated: (viewId) {
          initWebViewChannel(viewId);
        },
        viewType: 'plugins.flutter.test/webview',
        layoutDirection: TextDirection.ltr,
        creationParams: <String, dynamic>{
          'url': widget.url,
          'type': widget.iOSWebType
        },
        creationParamsCodec: const StandardMessageCodec(),
      );
    }*/ else {
      throw Exception("not impl:" + defaultTargetPlatform.toString());
    }
  }
}

  • 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

进行使用

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Container(
            child: LiuWebView(
                url: "https://www.baidu.com",
                onLoadStart: (url) {
                  showLoading();
                },
                onLoadStop: (url) {
                  dismissLoading();
                }
            ))
    );
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

OK,完工。这样,我们后期如果有更多方法想要让Flutter来调用,或者原生View的状态回调给Flutter,都可以实现了。

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

闽ICP备14008679号