概要
webview_flutterで特定リンクをアプリ内ブラウザで開く場合に、ホワイトリストやブラックリストを作って、アプリ内ブラウザを開く開かないの対応をすると思います。
その際に意図しないURLまでアプリ内ブラウザで開いてしまったので、その際に対応した内容を記載します。
アプリ内ブラウザを開く際にはurl_launcherを利用しています。
環境
> flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.17.5, on Mac OS X 10.15.5 19F101, locale ja-JP)
[!] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
✗ Android license status unknown.
Try re-installing or updating your Android SDK Manager.
See https://developer.android.com/studio/#downloads or visit visit https://flutter.dev/docs/get-started/install/macos#android-setup for detailed instructions.
[✓] Xcode - develop for iOS and macOS (Xcode 11.5)
[✓] Android Studio (version 3.6)
[✓] VS Code (version 1.46.1)
- webview_flutter: ^0.3.21
- url_launcher: ^5.4.7
実装内容
不要な部分は省略しています
main.dart
main.dart
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: WebView(
navigationDelegate:
// providerを使用しています
context.watch<WebViewViewmodel>().onNavigationDelegate,
)),
bottomNavigationBar: BottomNavigationWidget());
}
WebViewViewmodelの実装内容
WebViewViewmodel.dart
NavigationDecision onNavigationDelegate(NavigationRequest request) {
// request.urlに遷移先のURLが入ってくる
if (request.url == 'アプリ内ブラウザで開きたくない対象のURL') {
// navigateで通常のwebview内で遷移
return NavigationDecision.navigate;
} else {
log(request.url);
// アプリ内ブラウザで開きたい場合はurl_launcherを利用する
_launchBrowser(request.url);
return NavigationDecision.prevent;
}
}
// 詳細はurl_launcherの実装方法を参照
Future<void> _launchBrowser(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
throw ArgumentError('Could not launch $url');
}
}
問題点
実はこれだと問題がでてきます。
それはiframeやlink rel="xxx"で指定されるURLも判定対象になるためです。
iframeのURLやCSSのlink relの要素もnavigationDelegate
の処理に入ってきます。
そのため、意図しないURLもアプリ内ブラウザで開いてしまう可能性があります。
解決策
request.isForMainFrame
を利用します
WebViewViewmodel.dart
NavigationDecision onNavigationDelegate(NavigationRequest request) {
// request.isForMainFrameではない場合はwebviewでの遷移として扱う
if (!request.isForMainFrame) return NavigationDecision.navigate;
// ...略
}
名前の通り、isForMainFrameは遷移先のメインURLかどうかを判定してくれます。
iframeやlink relなどのURLの場合はfalse
が入るのでこれで対応できました。
皆さんの参考になれば幸いです。