Skip to content

[webview_flutter_wkwebview] Add support for custom URL scheme handlers#12236

Open
brlumen wants to merge 2 commits into
flutter:mainfrom
brlumen:pr/wkwebview-url-scheme-handler
Open

[webview_flutter_wkwebview] Add support for custom URL scheme handlers#12236
brlumen wants to merge 2 commits into
flutter:mainfrom
brlumen:pr/wkwebview-url-scheme-handler

Conversation

@brlumen

@brlumen brlumen commented Jul 18, 2026

Copy link
Copy Markdown

Adds support for loading resources with custom URL schemes on iOS and macOS by wrapping the native WKURLSchemeHandler API.

New public API:

  • WebKitWebViewControllerCreationParams.urlSchemeHandlers — a map of URL scheme to handler. Handlers are registered at creation time because WKWebViewConfiguration.setURLSchemeHandler only takes effect before the WKWebView is created.
  • WebKitUrlSchemeHandleronStart/onStop callbacks receiving a WebKitUrlSchemeTask.
  • WebKitUrlSchemeTask — exposes the request url/httpMethod and the reply methods didReceiveResponse, didReceiveData, didFinish, and didFail.

Internal changes:

  • New Pigeon ProxyApis: WKURLSchemeHandler (Dart-implemented, modeled after WKScriptMessageHandler) and WKURLSchemeTask; WKWebViewConfiguration.setURLSchemeHandler; constructors for the existing HTTPURLResponse, URLResponse, and NSError ProxyApis (needed to build responses from Dart).
  • Calling a WKURLSchemeTask method after the web view has stopped the task raises NSException. Since the stop notification inherently races in-flight platform channel calls, the native side tracks stopped tasks (via an associated object) and turns late calls into no-ops; the equivalent race on the Dart side (stop arriving while the start handler is still reading the request) is also handled.

Tests:

  • Dart unit tests for the new wrapper API.
  • Native XCTests for the new ProxyApi delegates, including the stopped-task no-op guard.
  • Integration tests in the example app covering: serving an image to a real WKWebView, stopping an in-flight task when the page is replaced (with late replies being no-ops), delivering a large (3 MB) response body via main frame navigation to a custom scheme URL, and 24 concurrent requests on one page.

Example use case: an email client that rewrites cid: inline-image references to a custom scheme and serves the bytes from its local attachment cache. WKWebView cannot intercept http(s): requests, so a custom scheme handler is the platform-sanctioned counterpart of Android's shouldInterceptRequest for serving locally cached subresources.

Part of flutter/flutter#27896 (implements the iOS/macOS side).

Pre-Review Checklist

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. 2

Adds WebKitWebViewControllerCreationParams.urlSchemeHandlers, wrapping the
native WKURLSchemeHandler API to load resources for custom URL schemes on
iOS and macOS. Also adds constructors for the HTTPURLResponse, URLResponse,
and NSError proxy APIs, needed to respond to scheme tasks from Dart.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements support for custom URL schemes in webview_flutter_wkwebview via the native WKURLSchemeHandler API, introducing WebKitUrlSchemeHandler and WebKitUrlSchemeTask along with their native Swift delegates and Pigeon bindings. The review feedback identifies a potential memory leak in _registerUrlSchemeHandler where tasks can remain in stoppedBeforeStart indefinitely, and recommends setting _stopped to true upon task completion to safeguard against post-completion calls.

@bparrishMines

Copy link
Copy Markdown
Contributor

@brlumen Does Android's WebViewClient.shouldInterceptRequest also handle custom URL schemes? If so then I think this feature would also require an Android implementation as well as an update to the platform interface and app-facing interface.

@brlumen

brlumen commented Jul 23, 2026

Copy link
Copy Markdown
Author

@bparrishMines Yes — shouldInterceptRequest does get invoked for custom URL schemes on Android, and a Dart-accessible counterpart there is something I'd very much like to have myself. In my app the chat screen has two implementations on Android: a native one (for unrelated historical reasons), where a shouldInterceptRequest interceptor serves locally cached email resources, and a webview_flutter-based one, which has no interception available and currently has to fall back to inlining resources as data URIs. An Android counterpart of this API would let the Flutter path use the same mechanism.

The catch is the threading contract. shouldInterceptRequest must return a WebResourceResponse synchronously on a Chromium worker thread, while platform channels are inherently asynchronous. As far as I can tell, there is still no way to synchronously invoke Dart from an arbitrary native thread (FFI callbacks are isolate-thread-only, and platform isolates never stabilized) — which I believe is the same conclusion reached in flutter/flutter#27896.

The only practical bridge is a bounded-wait latch: block the worker thread, post the request to Dart over the channel, and CountDownLatch.await with a timeout, degrading to an empty response on expiry. This is legal (the reply arrives on the unblocked main thread) and flutter_inappwebview has shipped this design for years, but it has real costs: every subresource pays a channel round-trip, and a busy Dart isolate stalls all in-flight resource loads for the page.

So before scoping this out, I'd like your take on the design question: is a latch-based shouldInterceptRequest bridge acceptable for webview_flutter_android in principle?

  • If yes — I'm happy to work on the federated version (platform interface + app-facing API + Android implementation) as a follow-up series.
  • If no (i.e. Android should wait for a proper synchronous-Dart mechanism) — then no Android implementation can exist yet regardless of how this feature is shaped, and I'd suggest landing the WebKit capability platform-specifically for now, following the precedent of other WebKit-only surface in WebKitWebViewControllerCreationParams (limitsNavigationsToAppBoundDomains, javaScriptCanOpenWindowsAutomatically, etc.). Notably WKURLSchemeHandler is also semantically narrower than shouldInterceptRequest (it cannot intercept http(s): at all), so a future shared interface would likely target only the custom-scheme subset anyway — and adding it later would be non-breaking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants