October 31, 2021

【Flutter】 InAppWebViewでリンクのクリックをハンドリングする

WebViewのサードパーティライブラリにInAppWebView というのがあります。

WebView内のリンクをクリックした際に、デフォルトのブラウザを開く等、なにかしら処理をしたいことがあると思います。 これまではJavaScriptを使ったりして頑張る必要があったのですが、JavaScriptをつかわずにリンクのクリックをフックする方法を解説します。
2021年10月の最新バージョンは5.3.2です。

Webページはパターンが多すぎるので抜け漏れがあるかもしれない点は気をつけてください🙏🏻

onCreateWindowshouldOverrideUrlLoading 2箇所で処理を書く必要があります。

onCreateWindow

onCreateWindow はWindowが生成される際に呼ばれます。
なんらかの処理をして、読み込みを中断する場合は Future<bool>.value(true); を呼び出してWebView自体のロードを中断するようにしましょう。

shouldOverrideUrlLoading

shouldOverrideUrlLoading はページ読み込みの際に呼ばれます。
有効にするにはまず crossPlatform のオプション経由でonにします。

crossPlatform: InAppWebViewOptions(
  useShouldOverrideUrlLoading: true,
),

このコールバックはリダイレクトや、iframe等のページ内の複数に読み込みに反応してしまいます。
そのためユーザのクリックによる読み込みなのか、自動的に読み込まれたのかを判定する必要があります。
AndroidはandroidHasGesture で判定します。 ただしAndroid7.0(API Level 24)以上しか正確に動作しないので注意してください。
iOSは navigationAction.iosWKNavigationTypeIOSWKNavigationType.LINK_ACTIVATED かどうかを判定します。

onCreateWindow 同様に読み込みを中断する場合はNavigationActionPolicy.ALLOWをreturnしましょう。

コード

InAppWebView(
  initialUrlRequest: URLRequest(url: Uri.parse(announcement.webUrl)),
  initialOptions: InAppWebViewGroupOptions(
    crossPlatform: InAppWebViewOptions(
      useShouldOverrideUrlLoading: true,
    ),
  ),
  onCreateWindow: (InAppWebViewController controller,
      CreateWindowAction onCreateWindowAction) async {
    final url = onCreateWindowAction.request.url;
    if (url != null) {
      final isOpened =
          await ref.read(_viewModel.notifier).openBrowser(url.toString());
      if (isOpened) {
        return Future<bool>.value(true);
      }
    }
    return Future<bool>.value(true);
  },
  shouldOverrideUrlLoading: (controller, navigationAction) async {
    // リンククリックイベントのみ
    if ((isIos &&
            navigationAction.iosWKNavigationType !=
                IOSWKNavigationType.LINK_ACTIVATED) ||
        (isAndroid && !(navigationAction.androidHasGesture ?? true))) {
      return NavigationActionPolicy.ALLOW;
    }
  
    final url = navigationAction.request.url;
    if (url != null) {
      final isOpened =
          await ref.read(_viewModel.notifier).openBrowser(url.toString());
      if (isOpened) {
        return NavigationActionPolicy.CANCEL;
      }
    }
    return NavigationActionPolicy.ALLOW;
  },
);

WebViewではなく、デフォルトのブラウザで開きたい場合はurl_launcher などのライブラリを使って開くと良いと思います。

© AAkira 2023