flutter_inappwebview overriding urls

My company is migrating its native app into a hybrid app using Flutter and Webview. A requirement is to only allow navigation inside the WebVew if the domain belongs to the company or the payment service that the app is using. For other URLs / links, they should be launched in the default browser of the mobile device (e.g. Chrome or Safari). For this app, we decided to use a WebView library called flutter_inappwebview.

Firstly, to intercept URLs and decide which of them to allow, you need to include options when initializing InAppWebView. In the case of flutter_inappwebview that option is called useShouldOverrideUrlLoading. For iOS WebKit, this function seems to interact with navigationDelegate.

InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
  crossPlatform: InAppWebViewOptions(
    // set to true. default is false
    useShouldOverrideUrlLoading: true,
    // needed for Android intent schemes
    resourceCustomSchemes: ['intent'],
    // ...
  ),
);

// ...
InAppWebView(
  initialOptions: options
  shouldOverrideUrlLoading: (controller, NavigationAction action) async {
    // logic goes here
    // return NavigationActionPolicy.CANCEL to prevent navigation
    // return NavigationActionPolicy.ALLOW to allow navigation
  }
  // ...
)

An issue when working with iOS WebKit was that it seemed to intercept URLs such as Firebase authentication, about:blank, etc. However, since those were requests needed for our website to function, we needed to allow its navigation rather than redirecting those links to a browser.

One way to fix the problem was to list all the external URLs needed for our website. However, that list would be too volatile. After some debugging and research, we found that NavigationAction parameter had IOSWKFrameInfo member variables for iosSourceFrame and iosTargetFrame. Both included the origin of the requested URL, and thus, we were about to use that origin to allow or deny navigation to a particular URL.

In short, we were able to write the following condition for iOS:

shouldOverrideUrlLoading: (controller, action) async {
  final Uri url;
  if(action.request.url != null){
    url = action.request.url!;
  } else {
    return NavigationActionPolicy.CANCEL;
  }

  if(url.host.contains(domain) || 
    action.iosSourceFrame?.securityOrigin?.host.contains(domain) == true) {
    return NavigationActionPolicy.ALLOW;  
  } else {
    launchUrl(url, mode: LaunchMode.externalApplication);
    return NavigationActionPolicy.CANCEL;  
  }

  // ...

}

* to launch URLs in a browser we used the url_launcher library.