Skip to content
WebView AllWebView All

Controller

WebViewController is the central object. A controller can be attached to one WebViewWidget at a time and delegates all work to the active platform implementation. On platforms with offscreen support, loading, navigation callbacks, JavaScript, and channels can also run before or without attaching a widget.

Use the default constructor for common behavior:

final controller = WebViewController();

Use fromPlatformCreationParams when the platform needs creation-time options:

PlatformWebViewControllerCreationParams params =
const PlatformWebViewControllerCreationParams();
if (WebViewPlatform.instance is LinuxWebViewPlatform) {
params = const LinuxWebViewControllerCreationParams(
developerExtrasEnabled: true,
pageCacheEnabled: true,
);
}
final controller = WebViewController.fromPlatformCreationParams(params);

Use fromPlatform only when you already constructed a platform controller yourself:

final platformController = WindowsWebViewController(
const WindowsWebViewControllerCreationParams(
popupWindowPolicy: WindowsPopupWindowPolicy.sameWindow,
),
);
final controller = WebViewController.fromPlatform(platformController);

Use an owning session when no visible WebView is needed:

import 'dart:async';
final session = await OffscreenWebViewSession.create();
final controller = session.controller;
final loaded = Completer<void>();
try {
await controller.setJavaScriptMode(JavaScriptMode.unrestricted);
await controller.setNavigationDelegate(
NavigationDelegate(onPageFinished: (_) => loaded.complete()),
);
await controller.loadHtmlString('<script>window.price = 42;</script>');
await loaded.future;
final price = await controller.callAsyncJavaScript('return window.price;');
} finally {
await session.close();
}

OffscreenWebViewSession.create throws UnsupportedError on OHOS and Web. Android, iOS, macOS, Windows, and Linux can operate without a widget; the owned controller may still be displayed before the session is closed. close() is idempotent and permanently releases the session resources without adding dispose() to the common controller API.

Offscreen execution still belongs to the application’s Flutter engine, platform thread, window, and process lifecycle. It is not a background service or a separate isolate, and operating-system background restrictions still apply. Do not retain or use the owned controller after close() starts.

Method Purpose Notes
loadRequest(Uri uri, {method, headers, body}) Load a URL or submit a request. uri must have a scheme. See platform limits below.
loadFile(String absoluteFilePath) Load a local file from the device. Unsupported on web.
loadFlutterAsset(String key) Load an asset declared in pubspec.yaml. Web resolves to assets/<key>.
loadHtmlString(String html, {String? baseUrl}) Load an in-memory HTML document. baseUrl is used for relative URLs.
Platform GET headers POST body POST custom headers
Android Supported Supported Not supported by Android postUrl; throws.
iOS Supported Supported Supported by URLRequest.
macOS Supported Supported Supported by URLRequest.
Windows Supported Supported Supported by WebView2 request bridge.
Linux Supported Supported Supported by WebKitGTK bridge.
OHOS Supported for GET Supported Not supported by ArkWeb postUrl; throws UnsupportedError.
Web Supported through fetch path Supported through fetch path Subject to CORS preflight and response policies.
final current = await controller.currentUrl();
final title = await controller.getTitle();
if (await controller.canGoBack()) {
await controller.goBack();
}
await controller.reload();

canGoBack and canGoForward reflect the platform history state. On web, webview_all_web maintains a logical history for loads initiated through the controller.

await controller.setJavaScriptMode(JavaScriptMode.unrestricted);
await controller.runJavaScript('document.body.dataset.ready = "true";');
final result = await controller.runJavaScriptReturningResult('1 + 2');

runJavaScriptReturningResult rejects null and undefined results, matching the platform interface contract. Complex JavaScript values must be serializable by the engine. The web implementation serializes through JSON.stringify.

await controller.addJavaScriptChannel(
'Host',
onMessageReceived: (JavaScriptMessage message) {
debugPrint(message.message);
},
);
await controller.runJavaScript('Host.postMessage("ping")');

Channel names must be valid JavaScript identifiers on native implementations that inject named objects. Avoid user-provided channel names unless you validate them.

await controller.scrollTo(0, 0);
await controller.scrollBy(0, 300);
final position = await controller.getScrollPosition();
await controller.setOnScrollPositionChange((ScrollPositionChange change) {
debugPrint('${change.x}, ${change.y}');
});

Scrollbar visibility is guarded by supportsSetScrollBarsEnabled():

if (await controller.supportsSetScrollBarsEnabled()) {
await controller.setVerticalScrollBarEnabled(false);
await controller.setHorizontalScrollBarEnabled(false);
}

Since webview_all 1.3.2, scrollbar rendering remains engine-specific. macOS reports this capability as unsupported because public WKWebView does not expose its internal scroll view. Web and Windows implement visibility with injected CSS.

Method Behavior
setBackgroundColor(Color color) Applies an engine background color where available. macOS uses a native API on 12+ and logs/no-ops on earlier versions.
enableZoom(bool enabled) Toggles platform zoom behavior. macOS uses native magnification.
setUserAgent(String? userAgent) Overrides the user agent where the engine allows it. Web logs once and ignores non-null overrides.
getUserAgent() Returns the effective or platform-reported user agent when available.
setOverScrollMode(WebViewOverScrollMode mode) Maps to native overscroll where possible or CSS overscroll-behavior on CSS-backed implementations. macOS logs/no-ops because no public API is available.

The platform field is the escape hatch for engine-specific APIs:

switch (controller.platform) {
case WindowsWebViewController windows:
await windows.openDevTools();
case LinuxWebViewController linux:
await linux.setDeveloperExtrasEnabled(true);
case OhosWebViewController ohos:
await ohos.setTextZoom(110);
case WebWebViewController web:
await web.setIFrameReferrerPolicy('no-referrer');
}

Always guard casts by platform type. Flutter tests, desktop runners, and web builds can register different platform implementations.