flutter_chromium_webview
A high-performance Chromium web view plugin for Flutter on Windows, macOS, Linux, and Android.
Desktop platforms bundle a pinned Chromium engine through CEF (Chromium Embedded Framework) with Off-Screen Rendering (OSR) directly into Flutter textures. Android integrates the system Chromium WebView using Flutter's hybrid composition platform view.
Target Release:
0.5.3
Companion platform interface:flutter_chromium_webview_platform_interface: ^0.1.6
Table of Contents
- Platform Support & Status
- Architecture Overview
- Requirements
- Installation & Setup
- Quick Start
- Core Features & Usage
- Lifecycle Management
- Navigation & HTML Loading
- JavaScript Evaluation & Execution
- Two-Way JavaScript Bridge
- Cookies, Storage & Named Profiles
- Multiple Simultaneous WebViews
- Input, Focus & Keyboard
- JavaScript Dialogs & Context Menus
- New Window Interception
- YouTube & Video Playback Integration
- Crash Recovery & Fault Tolerance
- Troubleshooting & Known Limitations
- Packages in this Repository
- Contributing & License
Platform Support & Status
| Platform | Engine | Rendering Path | Status | Guide |
|---|---|---|---|---|
| macOS 13+ | CEF 149 (Chromium 149) | Accelerated IOSurface via Metal | Supported (Apple Silicon arm64 & Intel x86_64) |
MACOS.md |
| Windows 10+ | CEF 149 (Chromium 149) | Software pixel buffer to Texture | Supported (x64, sandboxed bootstrap runner) |
WINDOWS.md |
| Linux | CEF 149 (Chromium 149) | Software pixel buffer to Texture | Supported (x64, X11 / XWayland via GTK3) |
LINUX.md |
| Android (API 24+) | System Chromium WebView | Native Platform View (Hybrid Composition) | Supported (ARM64, ARM32, x86_64) | ANDROID.md |
| iOS | WebKit (WKWebView) |
Headless prototype | Source only; not registered or supported | IOS.md |
| Web | N/A | N/A | Not implemented | N/A |
Architecture Overview
On desktop operating systems, flutter_chromium_webview uses Off-Screen Rendering (OSR):
- Chromium renders web contents completely off-screen in background helper processes.
- Rendered frames are passed to native host callbacks:
- On macOS, frames are delivered as hardware-accelerated IOSurface handles and bound to Metal textures.
- On Windows and Linux, pixel buffers are copied into a retained frame buffer and registered with the Flutter Engine's texture registry.
- The browser appears in your Flutter widget tree as a regular Flutter
Texture. It can be seamlessly clipped (e.g.ClipRRect), transformed, stacked, animated, or overlaid with other Flutter widgets. - On Android, rendering uses Flutter's native platform view mechanism (Hybrid Composition), providing native Android touch handling, accessibility, and virtual keyboard interaction.
Requirements
SDK Requirements
- Dart SDK:
>=3.12.0 <4.0.0 - Flutter SDK:
>=3.44.0
The package is engineered to be compatible with Dart >=3.12.0, allowing applications using Flutter 3.44+ (such as PPPlayer) to consume the published package without version conflicts. CI strictly tests across Flutter 3.47.4 / Dart 3.13.3.
OS & Toolchain Requirements
- macOS: macOS 13 (Ventura) or newer, Xcode 15+, CMake 3.19+, Python 3, CocoaPods.
- Windows: Windows 10/11 x64, Visual Studio 2022 (with C++ desktop development tools), CMake 3.19+.
- Linux: Linux x64 with GTK 3, Clang, CMake 3.19+, Ninja, pkg-config, X11 or XWayland, PulseAudio.
- Android: Android API 24+ (Android 7.0 Nougat), Android compile SDK 36, Java 17.
Installation & Setup
Add the package to your pubspec.yaml:
dependencies:
flutter_chromium_webview: ^0.5.3
Because desktop platforms require native runtime bootstrapping, follow the platform-specific instructions below:
macOS Configuration
macOS requires host embedding and runner configuration outside Apple's App Sandbox. For complete instructions, see MACOS.md.
- In
pubspec.yaml, disable Swift Package Manager for macOS:flutter: config: enable-swift-package-manager: false - In
macos/Runner/Info.plist, setNSPrincipalClasstoChromiumWebViewApplication. - In
macos/Runner/DebugProfile.entitlementsandmacos/Runner/Release.entitlements, setcom.apple.security.app-sandboxto<false/>. - Add the CEF preparation step to your
macos/Podfileand the embedding script to your Xcode Runner build phase as detailed in MACOS.md.
Windows Configuration
Windows uses CEF's sandboxed bootstrap executable runner. For complete instructions, see WINDOWS.md.
- In
windows/runner/CMakeLists.txt, change the runner target to a shared library (add_library(${BINARY_NAME} SHARED ...)). - In
windows/CMakeLists.txt, addchromium_webview_enable_sandbox(${BINARY_NAME}). - In
windows/runner/main.cpp, exportRunWinMainas described in WINDOWS.md.
Linux Configuration
Ensure system packages are installed:
sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev pulseaudio
On first build, CMake automatically downloads and verifies the pinned CEF Linux distribution. See LINUX.md.
Android Configuration
Ensure your android/app/build.gradle has minSdkVersion 24 and Java 17 compatibility. Internet permission is included automatically by the plugin manifest. See ANDROID.md.
Quick Start
Initialize the CEF runtime once in main() before creating any browser:
import 'package:flutter/material.dart';
import 'package:flutter_chromium_webview/flutter_chromium_webview.dart';
import 'package:path_provider/path_provider.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize CEF runtime on desktop. On Android, this initializes the WebView provider.
final cache = await getApplicationSupportDirectory();
final initialized = await ChromiumWebViewController.initialize(
cachePath: cache.path,
);
if (!initialized) {
throw StateError('Failed to initialize Chromium runtime');
}
runApp(const MaterialApp(home: WebViewDemo()));
}
class WebViewDemo extends StatefulWidget {
const WebViewDemo({super.key});
@override
State<WebViewDemo> createState() => _WebViewDemoState();
}
class _WebViewDemoState extends State<WebViewDemo> {
final controller = ChromiumWebViewController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Chromium WebView')),
body: ChromiumWebView(
controller: controller,
initialUrl: 'https://flutter.dev',
),
);
}
}
Core Features & Usage
Lifecycle Management
By default, the ChromiumWebView widget automatically disposes its controller when removed from the widget tree. If you want to keep the controller alive while navigating between tabs or screens, pass disposeController: false:
ChromiumWebView(
controller: controller,
disposeController: false, // You manage disposal explicitly
initialUrl: 'https://flutter.dev',
);
When you are done with the controller:
await controller.dispose();
Navigation & HTML Loading
// Load an arbitrary URL
await controller.loadUrl('https://dart.dev');
// Load raw HTML with an explicit base URL for origin and relative assets
await controller.loadHtmlString(
'<h1>Hello from Flutter!</h1><p>Running on Chromium.</p>',
baseUrl: 'https://example.com/app/',
);
// Navigation controls
if (controller.canGoBack) await controller.goBack();
if (controller.canGoForward) await controller.goForward();
await controller.reload();
await controller.stopLoading();
The controller extends ChangeNotifier, notifying listeners on URL changes, title changes, and progress updates:
ListenableBuilder(
listenable: controller,
builder: (context, _) {
return Text(controller.pageTitle ?? controller.currentUrl ?? 'Loading...');
},
);
JavaScript Evaluation & Execution
Execute fire-and-forget scripts or evaluate expressions and receive structured results:
// Fire-and-forget
await controller.executeJavaScript("console.log('Hello from Dart');");
// Evaluate expression with returned value
try {
final result = await controller.evaluateJavaScript('document.title');
print('Page title: $result');
// Objects and arrays are returned as parsed JSON structures
final dimensions = await controller.evaluateJavaScript('''
({
width: window.innerWidth,
height: window.innerHeight,
userAgent: navigator.userAgent
})
''');
print('Window info: $dimensions');
} catch (e) {
print('JavaScript execution error: $e');
}
Two-Way JavaScript Bridge
Communicate securely from web content back to Flutter using origin-restricted JavaScriptChannels:
final channel = JavaScriptChannel(
name: 'FlutterBridge',
allowedOrigins: {'https://flutter.dev', 'https://dart.dev'},
onMessageReceived: (JavaScriptMessage msg) {
print('Received message: ${msg.message}');
},
);
final controller = ChromiumWebViewController(
javaScriptChannels: [channel],
);
In the webpage JavaScript:
// Send a message to Flutter
FlutterBridge.postMessage(JSON.stringify({ event: 'button_clicked', id: 42 }));
Messages are protected by strict origin allowlists and capped at a maximum of 64 KiB UTF-8 to prevent buffer exhaustion.
Cookies, Storage & Named Profiles
Configure isolation, cookies, user-agent, and media gesture policies at controller construction:
final controller = ChromiumWebViewController(
profileName: 'isolated_user_profile', // Dedicated storage partition
userAgent: 'MyCustomApp/1.0 (Flutter Desktop)',
mediaPlaybackRequiresUserGesture: false, // Enable autoplay
backgroundColor: const Color(0xFF1E1E1E), // Dark canvas during navigation
);
- When
profileNameis omitted, the browser shares the global cache and cookie store. - Setting
mediaPlaybackRequiresUserGesture: falsecreates an ephemeral private context suitable for media playback without polluting persistent sessions.
Multiple Simultaneous WebViews
You can run multiple independent WebView instances simultaneously. Each has its own texture, controller, and event dispatching:
Row(
children: [
Expanded(child: ChromiumWebView(controller: controllerA, initialUrl: 'https://flutter.dev')),
const VerticalDivider(),
Expanded(child: ChromiumWebView(controller: controllerB, initialUrl: 'https://dart.dev')),
],
)
Input, Focus & Keyboard
- Mouse / Pointer: Coordinates, high-DPI device pixel ratios, scroll wheels, and multi-click counts (double-clicks, triple-clicks) are forwarded automatically to the native browser.
- Keyboard & Shortcuts: Key events, Cmd/Ctrl editing shortcuts (Copy, Cut, Paste, Select All), and IME composition are handled natively.
JavaScript Dialogs & Context Menus
Customize the presentation of JavaScript dialogs (alert, confirm, prompt) and right-click context menus using Flutter UI:
controller.onJSDialog = (JSDialogRequest request) async {
final confirmed = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
title: Text(request.type.name.toUpperCase()),
content: Text(request.message),
actions: [
TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('Cancel')),
FilledButton(onPressed: () => Navigator.pop(ctx, true), child: const Text('OK')),
],
),
);
return JSDialogResponse(success: confirmed ?? false);
};
controller.onContextMenu = (List<ContextMenuItem> items) {
// Render Flutter popup or context menu using the provided command IDs
};
New Window Interception
Intercept window.open or target _blank links:
controller.onNewWindowRequested = (NewWindowRequest request) {
print('Requested popup URL: ${request.url}');
// Open in a new tab, dialog, or external browser
};
YouTube & Video Playback Integration
For video playback, the plugin includes a dedicated ChromiumYoutubePlayerController that wraps YouTube's IFrame API with native telemetry, autoplay support, and state synchronization:
import 'package:flutter_chromium_webview/chromium_youtube_player.dart';
final player = ChromiumYoutubePlayerController(
documentUrl: 'https://example.com/player',
readyTimeout: const Duration(seconds: 15),
);
// In your widget:
ChromiumWebView(
controller: player.webViewController,
disposeController: false,
);
// Control playback:
await player.loadVideoById('dQw4w9WgXcQ');
await player.play();
await player.pause();
await player.seekTo(Duration(seconds: 45));
Supported open video codecs: VP8, VP9, AV1, Vorbis, Opus. Standard CEF does not include proprietary codecs like H.264 or AAC.
Crash Recovery & Fault Tolerance
If the Chromium renderer or GPU subprocess crashes, flutter_chromium_webview detects the failure and fires onBrowserCrashed:
controller.onBrowserCrashed = () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Browser process crashed. Reloading...')),
);
controller.reload();
};
Troubleshooting & Known Limitations
- Proprietary Codecs: Stock CEF builds distributed by the project do not include H.264 or AAC codecs due to patent and licensing restrictions. Media encoded with VP8, VP9, or AV1 is recommended.
- Apple App Sandbox: The macOS CEF backend currently requires direct distribution outside Apple's App Sandbox. See MACOS.md.
- Process Shutdown: Once CEF has shut down via
CefShutdown(), CEF cannot be reinitialized in the same process without restarting the host application. - macOS Memory Bounds: Extended validation confirms clean tracked resource disposal and zero surviving processes on shutdown. However, long-term leak freedom and physical 8 GB qualification remain open product items.
- Application Bundle Size: Bundling Chromium and CEF increases the application distribution size by approximately 100–130 MB.
Packages in this Repository
flutter_chromium_webview: The main Flutter plugin consumed by applications.flutter_chromium_webview_platform_interface: The platform interface and method channel contracts.
Contributing & License
Contributions are welcome! Please review CONTRIBUTING.md and docs/architecture.md.
This project is licensed under the MIT License.
Chromium and CEF are covered by their respective BSD-style licenses.
Libraries
- chromium_youtube_player
- flutter_chromium_webview
- A Chromium web view using CEF on desktop and the system WebView on Android.