项目背景
之前我介绍过Flutter建立JsBridge用于webview与h5通信,有时候h5页面中需要唤醒其他应用的功能,通过约定的Scheme协议,比如weixin://,这时候就需要我们的Webview组件处理,否则就会出现net:ERR_UNKNOWN_URL-SCHEME的页面报错。
插件变更
上篇介绍Webview使用的文章,我用的是官方插件 webview_flutter,实际应用的时候,出现了个难以解决的bug,在华为mate30、android10环境下,h5的input元素调取手机系统键盘会失败,据我们测试,目前只发现在该手机型号、安卓系统下出现该问题,而华为客户占比很重,是必须要解决的,github上有类似的closed issue,推荐使用flutter_webview_plugin,所以就更改了webview插件,这两个插件评分都很高,提供的api类似,使用方法也相似,组件改造工作量很小。
组件改造
1、安装webview_flutter
flutter_webview_plugin: ^0.3.10+1
2、 ios中在ios/Runner/Info.plist中添加
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict>
3、改造WebView组件
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
import 'package:url_launcher/url_launcher.dart';
import '../configs/config.dart';
import '../utils/adapter.dart';
import '../utils/JsBridgeUtil.dart';
import '../widgets/AppIcon.dart';
import '../widgets/FixedSizeText.dart';
/// 与h5 端的一致 不然收不到消息
const String userAgent = 'FoxApp';
/// WebView页面
class Webview extends StatefulWidget {
final String url;
final String title;
final VoidCallback backCallback;
Webview({
Key key,
@required this.url,
this.title = '',
this.backCallback,
}) : super(key: key);
@override
WebviewState createState() => WebviewState();
}
class WebviewState extends State<Webview> {
bool isPhone = Adapter.isPhone();
final flutterWebViewPlugin = FlutterWebviewPlugin();
final Set<JavascriptChannel> jsChannels = [
JavascriptChannel(
name: userAgent,
onMessageReceived: (JavascriptMessage msg) {
String jsonStr = msg.message;
JsBridgeUtil.executeMethod(JsBridgeUtil.parseJson(jsonStr));
}),
].toSet();
// On urlChanged stream
StreamSubscription<String> _onUrlChanged;
@override
void initState() {
super.initState();
flutterWebViewPlugin.close();
// Add a listener to on url changed
_onUrlChanged =
flutterWebViewPlugin.onUrlChanged.listen((String url) async {
if (url.contains('weixin:') || url.contains('alipay:')) {
await flutterWebViewPlugin.stopLoading();
await flutterWebViewPlugin.goBack();
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
});
}
@override
void dispose() {
_onUrlChanged.cancel();
flutterWebViewPlugin.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: isPhone ? Colors.white : Color(Config.foxColors.bg),
body: WebviewScaffold(
appBar: AppBar(
backgroundColor: isPhone ? null : Color(Config.foxColors.bg),
leading: AppIcon(Config.foxImages.backGreyUrl, callback: () {
flutterWebViewPlugin.close();
Navigator.of(context).pop(true);
widget.backCallback?.call();
}),
title: FixedSizeText(widget.title),
centerTitle: true,
elevation: 0,
),
url: widget.url,
userAgent: "Mozilla/5.0 $userAgent",
// h5 可以通过navigator.userAgent判断当前环境
javascriptChannels: jsChannels,
mediaPlaybackRequiresUserGesture: false,
withZoom: true,
withLocalStorage: true,
hidden: true,
),
);
}
}
测试同学提出了一个bug,跳转微信时,会偶现net:ERR_UNKNOWN_URL-SCHEME页面,这条issue也在讨论这个问题,https://github.com/fluttercommunity/flutter_webview_plugin/issues/43,本质是插件提供的功能不完善,没有提供拦截页面显示的方法,社区提出了一些解决方式。
/// 兼容android进入报错页
_onStateChanged = flutterWebViewPlugin.onStateChanged
.listen((WebViewStateChanged state) async {
if (mounted) {
if (state.url.startsWith('weixin:') &&
state.type == WebViewState.abortLoad) {
if (await canLaunch(state.url)) {
await launch(state.url);
} else {
throw 'Could not launch ${state.url}';
}
}
}
});