赞
踩
在Flutter开发过程中,我们发现Flutter对于WebView的支持并不太好,而我们原生项目中,使用的腾讯的X5 WebView,所以也想在Flutter中进行嵌入使用。
具体的操作步骤如下
新建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()
}
}
新建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
}
}
新建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())
)
}
}
}
接着,我们需要注册这个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());
});
如果没有使用
FlutterBoost
,可以在继承自FlutterActivity
的代理Activity中进行注册,具体可以看我的另一篇博客 Flutter和Native之间进行通信
新建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());
}
}
}
最后,在需要的页面里进行使用即可
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"),
));
}
}
如果我们需要将原生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
}
}
}
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());
}
}
}
进行使用
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();
}
))
);
}
}
OK,完工。这样,我们后期如果有更多方法想要让Flutter来调用,或者原生View的状态回调给Flutter,都可以实现了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。