我使用 flutter 的
web_view_plugin
(webview)构建了一个混合应用程序。
我们的一种付款方式需要打开第 3 方应用程序(在本例中为 kakaotalk)。但是flutter webview插件不提供这个功能,返回了net::ERR_UNKNOWN_URL_SCHEME
。我做了一些研究,我明白问题出在url
。如果url
不是以http
或https
开头,就会导致这个错误。
因此,为了解决这个问题,我必须更改本机java代码。现在我对
java
和android
完全没有经验,所以修复原生代码非常困难。我知道我必须修改 shouldOverrideUrlLoading
部分,以便允许以 url
开头的 intent://
,而且我还必须进行一些验证来检查应用程序是否已安装。(如果未安装,用户应该被重定向到 Playstore)
我添加的代码在
shouldOverrideUrlLoading
中。
我还做了三个导入。剩下的就是代码,由 flutter 生成
package com.flutter_webview_plugin;
import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.os.Build;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.content.Intent; //added import
import android.net.Uri; //added import
import android.content.ActivityNotFoundException; //added import
/**
* Created by lejard_h on 20/12/2017.
*/
public class BrowserClient extends WebViewClient {
private Pattern invalidUrlPattern = null;
public BrowserClient() {
this(null);
}
public BrowserClient(String invalidUrlRegex) {
super();
if (invalidUrlRegex != null) {
invalidUrlPattern = Pattern.compile(invalidUrlRegex);
}
}
public void updateInvalidUrlRegex(String invalidUrlRegex) {
if (invalidUrlRegex != null) {
invalidUrlPattern = Pattern.compile(invalidUrlRegex);
} else {
invalidUrlPattern = null;
}
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
Map<String, Object> data = new HashMap<>();
data.put("url", url);
data.put("type", "startLoad");
FlutterWebviewPlugin.channel.invokeMethod("onState", data);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
Map<String, Object> data = new HashMap<>();
data.put("url", url);
FlutterWebviewPlugin.channel.invokeMethod("onUrlChanged", data);
data.put("type", "finishLoad");
FlutterWebviewPlugin.channel.invokeMethod("onState", data);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
// returning true causes the current WebView to abort loading the URL,
// while returning false causes the WebView to continue loading the URL as usual.
String url = request.getUrl().toString();
boolean isInvalid = checkInvalidUrl(url);
Map<String, Object> data = new HashMap<>();
data.put("url", url);
data.put("type", isInvalid ? "abortLoad" : "shouldStart");
FlutterWebviewPlugin.channel.invokeMethod("onState", data);
return isInvalid;
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// returning true causes the current WebView to abort loading the URL,
// while returning false causes the WebView to continue loading the URL as usual.
if (url.startsWith(INTENT_PROTOCOL_START)) {
final int customUrlStartIndex = INTENT_PROTOCOL_START.length();
final int customUrlEndIndex = url.indexOf(INTENT_PROTOCOL_INTENT);
if (customUrlEndIndex < 0) {
return false;
} else {
final String customUrl = url.substring(customUrlStartIndex, customUrlEndIndex);
try {
view.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(customUrl)));
} catch (ActivityNotFoundException e) {
final int packageStartIndex = customUrlEndIndex + INTENT_PROTOCOL_INTENT.length();
final int packageEndIndex = url.indexOf(INTENT_PROTOCOL_END);
final String packageName = url.substring(packageStartIndex, packageEndIndex < 0 ? url.length() : packageEndIndex);
view.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(GOOGLE_PLAY_STORE_PREFIX + packageName)));
}
return true;
}
} else {
return false;
}
// boolean isInvalid = checkInvalidUrl(url);
// Map<String, Object> data = new HashMap<>();
// data.put("url", url);
// data.put("type", isInvalid ? "abortLoad" : "shouldStart");
// FlutterWebviewPlugin.channel.invokeMethod("onState", data);
// return isInvalid;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
super.onReceivedHttpError(view, request, errorResponse);
Map<String, Object> data = new HashMap<>();
data.put("url", request.getUrl().toString());
data.put("code", Integer.toString(errorResponse.getStatusCode()));
FlutterWebviewPlugin.channel.invokeMethod("onHttpError", data);
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
Map<String, Object> data = new HashMap<>();
data.put("url", failingUrl);
data.put("code", errorCode);
FlutterWebviewPlugin.channel.invokeMethod("onHttpError", data);
}
private boolean checkInvalidUrl(String url) {
if (invalidUrlPattern == null) {
return false;
} else {
Matcher matcher = invalidUrlPattern.matcher(url);
return matcher.lookingAt();
}
}
}
代码可以编译,但当我尝试使用“第 3 方应用程序(kakaotalk)”付款时,它仍然返回相同的错误
net::ERR_UNKNOWN_URL_SCHEME
我之前在 Android 上遇到过类似的错误,当时 Firebase 动态链接被强制加载到 WebView 中。就我而言,FDL 预计将由 Android 中的 Google Play 服务处理。但由于 WebView 不知道如何处理它强制显示的链接,因此 WebView 返回“net::ERR_UNKNOWN_URL_SCHEME”错误。我不确定这是否与您的情况相同,因为除了“intent://kakaopay...”之外,我无法验证您尝试加载的链接
url_launcher
从外部打开链接。使用 RegEx 过滤意图 URL 并检查 URL 是否可以启动并在外部(应用程序外部)处理。
var yourURL = "URL goes here";
// Check if URL contains "intent"
yourURL.contains(RegExp('^intent://.*\$')){
// Check if the URL can be launched
if (await canLaunch(yourURL)) {
await launch(yourURL);
} else {
print('Could not launch $yourURL');
}
}
此外,您使用的插件(
web_view_plugin
)似乎已过时,我在这里找不到它https://pub.dev/packages?q=web_view_plugin。 Flutter 有其官方 WebView 插件 (webview_flutter
) 已发布,我建议检查它是否适合您的用例。
听着,在某些情况下使用@omatt的答案可能不起作用,特别是对于webview_flutter。我努力寻找解决方案,所以我这样做了:
_launchURL(url) async {
var link = "https://hiddenwords.page.link/deposit";
if (await canLaunch(link)) {
await launch(link,
forceWebView: false, enableJavaScript: true, forceSafariVC:
false);
} else {
throw 'Could not launch $link';
}
}
我手动将我想要它打开的网址/链接放在 _launch 函数中...不要介意 _launch 括号中的网址。
我还将其添加到 Webview 小部件中:
navigationDelegate: (NavigationRequest request) {
if (request.url.contains(RegExp('^intent://.*\$'))) {
_launchURL(request.url);
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
希望这对您有用。它对我有用...
1.使用您的APP在flutter中使用参数(在您的动态链接中)打开其他应用程序; 2.使用:url_launcher:^6.1.6;
首先,他们的应用程序必须支持动态链接; 其次,他们为您提供他们的交易的动态链接; 这样我们就可以点击你APP中的动态链接,跳转到他们APP的指定页面了。
代码:
final Uri toLaunch = Uri(scheme: 'https', host: 'link.fitstop.com', path: 'link/qbvQ/');
//https://link.fitstop.com/link/qbvQ is dynamic link
Future<void>? _launched;
ElevatedButton(
onPressed: () => setState(() {
_launched = _launchInBrowser(toLaunch);
}),
child: Text(
'url_launcher',
),
)
Future<void> _launchInBrowser(Uri url) async {
if (!await launchUrl(
url,
mode: LaunchMode.externalApplication,
)) {
throw 'Could not launch $url';
}
}
您可以使用flutter_inappwebview包
InAppWebViewSettings settings = InAppWebViewSettings(
useShouldOverrideUrlLoading: true,
...
);
@override
Widget build(BuildContext context) {
super.build(context);
return
...
InAppWebView(
...
shouldOverrideUrlLoading: (controller, navigationAction) async {
var uri = navigationAction.request.url!;
if (uri.scheme == "intent") {
return NavigationActionPolicy.CANCEL;
} else {
return NavigationActionPolicy.ALLOW;
}
},
...
),
...
}