在Flutter中使用webview,在pub.dev中查看,有几个组件都可以使用webview
本文使用的是webview_flutter,它是Flutter团队开发的,目前还不是正式版,但已经可以使用。在iOS中底层调用的是WKWebView
,在Android中底层调用的是WebView
。
webview_flutter 使用
- 添加组件 添加webview_flutter
- 主要代码
WebView(
initialUrl: "https://flutterchina.club/",
//JS执行模式 是否允许JS执行
javascriptMode: JavascriptMode.unrestricted,
)
- 针对iOS,需要在ios-Runner-info.plist中添加
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>
第三步不可少,否则会报错。
Flutter调用JS
在调用之前 先看一下WebView的其他参数:
WebView创建完成时调用
onWebViewCreated
,
要显示的url
initialUrl
JS执行模式 默认是不调用
javascriptMode = JavascriptMode.disabled
使用javascriptChannel JS可以调用Flutter
javascriptChannels
拦截请求
navigationDelegate
手势
gestureRecognizers
页面加载完成
onPageFinished
通过WebViewController
调用evaluateJavascript
方法,即可调用JS。
了解了每个参数的作用之后,交互就看似简单了。
Flutter调用JS的流程:
-
WebView
创建完成时在onWebViewCreated
中,获取到WebViewController
实例。 - 当页面加载完成之后,即
onPageFinished
之后,通过WebViewController
的evaluateJavascript
方法调用JS。 -
evaluateJavascript
返回的是Future<String>
,通过Future
可以获取到JS的返回结果。
示例代码: 在页面加载完成之后,获取网页标题,并显示在导航栏上。
WebView(
initialUrl: "https://flutterchina.club/",
//JS执行模式 是否允许JS执行
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (controller) {
_controller = controller;
},
onPageFinished: (url) {
_controller.evaluateJavascript("document.title").then((result){
setState(() {
_title = result;
});
}
);
},
)
效果:
JS调用Flutter
JS调用Flutter主要看剩下的两个参数:
javascriptChannels
navigationDelegate
这两个参数都可以从JS调用Flutter,可以单独使用,也可以混合使用。
javascriptChannels使用:
javascriptChannels
类似于往JS中注册方法,这些方法名要和web端约定好。
javascriptChannels
参数接受Set<JavascriptChannel>
一个成员类型为JavascriptChannel
的Set集合。
来看一下JavascriptChannel
的用法:
JavascriptChannel(
name: "share",
onMessageReceived: (JavascriptMessage message) {
print("参数为: ${message.message}");
}
)
在JS端就可以调用share
方法,同时可以传递参数,在Flutter中通过JavascriptMessage
可以获取到参数。
navigationDelegate使用:
navigationDelegate: (NavigationRequest request) {
//对于需要拦截的操作 做判断
if(request.url.startsWith("myapp://")) {
print("即将打开 ${request.url}");
//做拦截处理
//pushto....
Application.push(context, request.url);
return NavigationDecision.prevent;
}
//不需要拦截的操作
return NavigationDecision.navigate;
} ,
例如想要通过webview打开app的原生页面,通过约定好的链接,拦截到指定链接后可跳转到原生页面。
完整示例代码:
import 'package:flutter/cupertino.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebViewPage extends StatefulWidget {
@override
_WebViewPageState createState() => _WebViewPageState();
}
class _WebViewPageState extends State<WebViewPage> {
WebViewController _controller;
String _title = "webview";
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text("$_title"),
),
child: SafeArea(
child: WebView(
initialUrl: "https://flutterchina.club/",
//JS执行模式 是否允许JS执行
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (controller) {
_controller = controller;
},
onPageFinished: (url) {
_controller.evaluateJavascript("document.title").then((result){
setState(() {
_title = result;
});
}
);
},
navigationDelegate: (NavigationRequest request) {
if(request.url.startsWith("myapp://")) {
print("即将打开 ${request.url}");
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
} ,
javascriptChannels: <JavascriptChannel>[
JavascriptChannel(
name: "share",
onMessageReceived: (JavascriptMessage message) {
print("参数: ${message.message}");
}
),
].toSet(),
),
),
);
}
}
可能已经有同学看出来了,JS调用Flutter时,JS获取不到Flutter的返回值,目前还没有找到直接的解决办法,但可以通过迂回的方法解决。
下面提供两种思路:
- js调用Flutter时,除了传递业务需要的参数之外,再添加一个callbackname参数
通过callbackname参数把JS调用Flutter和Flutter返回参数绑定起来。
JavascriptChannel(
name: "share",
onMessageReceived: (JavascriptMessage message) {
print("参数: ${message.message}");
String callbackname = message.message; //实际应用中要通过map通过key获取
String data = "收到消息调用了";
String script = "$callbackname($data)";
_controller.evaluateJavascript(script);
}
)
- 在网页加载成功后,把JS需要获取的Flutter的值,注入到widow上, 在JS中就可以通过widow获取到相应的值。
String script = "window.isLogin=是否登录";
_controller.evaluateJavascript(script).then((result){
}
);