日常咱们在开发项目时,为了项目快速的开发和迭代,难免会用到H5页面。运用Flutter进行项目开发时,也相同免不了要加载H5页面,在移动开发中翻开H5页面需求运用WebView
组件。一起,为了和H5页面进行数据交换,有时分还需求凭仗JSBridge
来结束客户端与H5之间的通讯。
添加 webview_flutter 组件
在项目的pubspec.yaml
文件中添加依靠:webview_flutter: ^3.0.0
,然后实行pub get
。
- 由于加载WebView需求运用网络,所以还需求在android中添加网络权限。翻开目录android/app/src/main/AndroidManifest.xml,然后添加如下代码即可。
<uses-permission android:name="android.permission.INTERNET"/>
- 由于iOS在9.0版别默许敞开了Https,所以要工作Http的网页,还需求在ios/Runner/Info.plist文件中添加如下代码。
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>
webview_flutter 组件的结构方法的简单介绍
WebView({
Key key,
this.onWebViewCreated, //WebView创建结束之后的回调
this.initialUrl, // 初始化 URL
this.javascriptMode = JavascriptMode.disabled, //JS实行形式,默许是不调用
this.javascriptChannels, // JS可以调用Flutter 的通道
this.navigationDelegate, // 路由托付,可以运用它实行拦截操作
this.gestureRecognizers, // 手势监听相关
this.onPageStarted, //初步加载页面回调
this.onPageFinished, // 页面加载结束的回调
this.onWebResourceError, //资源加载失利回调
this.debuggingEnabled = false,
this.gestureNavigationEnabled = false,
this.userAgent,
this.initialMediaPlaybackPolicy =
AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
})
运用 Webview 加载网页时,许多时分需求与JS进行交互,即JS调用Flutter
和Flutter调用JS
。
检查官方文档,发现供给的能力,在实践开发中可以参阅这些功用。包括:网络央求、Cookies相关、缓存相关、加载html、加载失利页面
等等。
Future<void> _onShowUserAgent(
WebViewController controller, BuildContext context) async {
// Send a message with the user agent string to the Toaster JavaScript channel we registered
// with the WebView.
await controller.runJavascript(
'Toaster.postMessage("User Agent: " + navigator.userAgent);');
}
Future<void> _onListCookies(
WebViewController controller, BuildContext context) async {
final String cookies =
await controller.runJavascriptReturningResult('document.cookie');
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(SnackBar(
content: Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Cookies:'),
_getCookieList(cookies),
],
),
));
}
Future<void> _onAddToCache(
WebViewController controller, BuildContext context) async {
await controller.runJavascript(
'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";');
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(const SnackBar(
content: Text('Added a test entry to cache.'),
));
}
Future<void> _onListCache(
WebViewController controller, BuildContext context) async {
await controller.runJavascript('caches.keys()'
'.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
'.then((caches) => Toaster.postMessage(caches))');
}
Future<void> _onClearCache(
WebViewController controller, BuildContext context) async {
await controller.clearCache();
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(const SnackBar(
content: Text('Cache cleared.'),
));
}
Future<void> _onClearCookies(BuildContext context) async {
final bool hadCookies = await cookieManager.clearCookies();
String message = 'There were cookies. Now, they are gone!';
if (!hadCookies) {
message = 'There are no cookies.';
}
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(message),
));
}
Future<void> _onNavigationDelegateExample(
WebViewController controller, BuildContext context) async {
final String contentBase64 =
base64Encode(const Utf8Encoder().convert(kNavigationExamplePage));
await controller.loadUrl('data:text/html;base64,$contentBase64');
}
Future<void> _onSetCookie(
WebViewController controller, BuildContext context) async {
await CookieManager().setCookie(
const WebViewCookie(
name: 'foo', value: 'bar', domain: 'httpbin.org', path: '/anything'),
);
await controller.loadUrl('https://httpbin.org/anything');
}
Future<void> _onDoPostRequest(
WebViewController controller, BuildContext context) async {
final WebViewRequest request = WebViewRequest(
uri: Uri.parse('https://httpbin.org/post'),
method: WebViewRequestMethod.post,
headers: <String, String>{'foo': 'bar', 'Content-Type': 'text/plain'},
body: Uint8List.fromList('Test Body'.codeUnits),
);
await controller.loadRequest(request);
}
Future<void> _onLoadLocalFileExample(
WebViewController controller, BuildContext context) async {
final String pathToIndex = await _prepareLocalFile();
await controller.loadFile(pathToIndex);
}
Future<void> _onLoadFlutterAssetExample(
WebViewController controller, BuildContext context) async {
await controller.loadFlutterAsset('assets/www/index.html');
}
Future<void> _onLoadHtmlStringExample(
WebViewController controller, BuildContext context) async {
await controller.loadHtmlString(kLocalExamplePage);
}
Future<void> _onTransparentBackground(
WebViewController controller, BuildContext context) async {
await controller.loadHtmlString(kTransparentBackgroundPage);
}
Widget _getCookieList(String cookies) {
if (cookies == null || cookies == '""') {
return Container();
}
final List<String> cookieList = cookies.split(';');
final Iterable<Text> cookieWidgets =
cookieList.map((String cookie) => Text(cookie));
return Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: cookieWidgets.toList(),
);
}
static Future<String> _prepareLocalFile() async {
final String tmpDir = (await getTemporaryDirectory()).path;
final File indexFile = File(
<String>{tmpDir, 'www', 'index.html'}.join(Platform.pathSeparator));
await indexFile.create(recursive: true);
await indexFile.writeAsString(kLocalExamplePage);
return indexFile.path;
}
JS调用Flutter
navigationDelegate 方法结束
这种方法结束的原理主要是加载网页的时分进行拦截
。
下面举例结束的代码
js代码:
document.location = "js://webview?name=candy";
flutter 端代码:
navigationDelegate: (NavigationRequest request) {
if(request.url.startsWith("js://webview")) {
print("初步处理 ${request.url}");
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
这儿的 NavigationDecision.prevent
标明阻挠路由替换,NavigationDecision.navigate
标明答应路由替换。
javascriptChannels 方法结束
js代码:
<button onclick="callFlutter()">callFlutter</button>
function callFlutter(){
Toast.postMessage("js call flutter");
}
flutter 端代码:
WebView(
javascriptChannels: <JavascriptChannel>[
_shareJavascriptChannel(context),
].toSet(),
)
JavascriptChannel _shareJavascriptChannel(BuildContext context) {
return JavascriptChannel(
name: 'share',
onMessageReceived: (JavascriptMessage message) {
print("参数: ${message.message}");
showToast(message.message);
});
}
Flutter 调用 JS
js代码:
function callJS(message){
document.getElementById("p1").style.visibility = message;
}
Flutter 代码
Future<void> evaluateJavascript() async {
print('evaluateJavascript');
_controller.runJavascript('callJS('visible');');
}
加载本地 html
html 代码
<!DOCTYPE html>
<html>
<body>
<style>*{font-size:50px;}</style>
<button onclick="callFlutter()">callFlutter</button>
<p id="p1" style="visibility:hidden;">
Flutter代码调用了JS方法.
</p>
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="http://cdn.amazeui.org/amazeui/2.5.0/js/amazeui.min.js"></script>
<script type="text/javascript">
function callJS(message){
document.getElementById("p1").style.visibility = message;
}
</script>
<script type="text/javascript">
function callFlutter(){
Toaster.postMessage('js call flutter');
}
</script>
</body>
</html>
Future<void> _loadHtmlFromAsset() async {
String html = 'assets/static/test.html';
final String path = await rootBundle.loadString(html);
_controller.loadUrl(Uri.dataFromString(path,
mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
.toString());
}
完好结束代码
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebViewPage extends StatefulWidget {
@override
_WebViewPageState createState() => _WebViewPageState();
}
class _WebViewPageState extends State<WebViewPage> {
late WebViewController _controller;
String _title = "webview";
//加载Html
Future<void> _loadHtmlFromAsset() async {
String html = 'assets/static/test.html';
final String path = await rootBundle.loadString(html);
_controller.loadUrl(Uri.dataFromString(path,
mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
.toString());
}
@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;
_loadHtmlFromAsset();
},
onPageFinished: (url) async{
//调用JS方法,获取页面的标题
String title = await _controller.runJavascriptReturningResult('document.title');
setState(() {
_title = title;
});
evaluateJavascript();
},
navigationDelegate: (NavigationRequest request) {
if(request.url.startsWith("js://webview")) {
print("初步处理 ${request.url}");
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
javascriptChannels: <JavascriptChannel>{
JavascriptChannel(
name: "share",
onMessageReceived: (JavascriptMessage message) {
print("参数: ${message.message}");
//实践运用中要通过map通过key获取
String callbackname = message.message;
String data = "收到音讯调用了";
String script = "$callbackname($data)";
_controller.runJavascript(script);
}
),
},
),
),
);
}
Future<void> evaluateJavascript() async {
print('evaluateJavascript');
//这个是结束了Flutter操控了H5页面文本的闪现
_controller.runJavascript('callJS('visible');');
}
}
加载网页
给这个属性赋值就可以了
initialUrl: 'https://flutterchina.club/',
页面是否可以回退
Future<bool> _goBack(BuildContext context) async {
if (_controller != null && await _controller.canGoBack()) {
_controller.goBack();
return false;
}
return true;
}
官方结束了更多的功用:pub.dev/packages/we…
以上就讲解了 webView 的网页加载、JS交互、网络央求、Cookies相关、缓存相关、加载html、加载失利页面等功用。