flutterのwebview_flutterでリンクをアプリ内ブラウザで開きたい場合の注意点

最終更新日

アイキャッチ

概要

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: SafeArea(
            child: WebView(
          navigationDelegate:
              // providerを使用しています
              context.watch<WebViewViewmodel>().onNavigationDelegate,
        )),
        bottomNavigationBar: BottomNavigationWidget());
  }

WebViewViewmodelの実装内容

  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を利用します

  NavigationDecision onNavigationDelegate(NavigationRequest request) {
    // request.isForMainFrameではない場合はwebviewでの遷移として扱う
    if (!request.isForMainFrame) return NavigationDecision.navigate;
    // ...略
  }

名前の通り、isForMainFrameは遷移先のメインURLかどうかを判定してくれます。
iframeやlink relなどのURLの場合はfalseが入るのでこれで対応できました。


皆さんの参考になれば幸いです。

stmon19

遊びが一番 人生遊び 好きにまみれてます