flutter_webview_communication
A Flutter plugin for creating WebViews with seamless bi-directional communication between Flutter and JavaScript, built on top of the webview_flutter package (version 4.8.0).

Table of Contents
- flutter_webview_communication
Features
- Load custom HTML content or external URLs.
- Inject custom CSS (for HTML content) and JavaScript (embedded for HTML, injected post-load for URLs).
- Bi-directional JSON-based communication between Flutter and WebView with action-based message handling.
- Manage WebView local storage: save, retrieve, remove, and clear key-value pairs.
- Apply Content Security Policy (CSP) to custom HTML for enhanced security.
- Set custom user agent strings for WebView requests.
- Monitor loading states (started, progress, finished, error) with progress percentages and HTTP error details.
- Retrieve the current scroll position of the WebView using JavaScript.
- Clear cookies, cache, and local storage with a single method.
- Reload the current WebView content (HTML or URL).
- Comprehensive JavaScript error handling via a callback.
- NEW: Navigation controls - Back, forward, stop loading, get URL/title.
- NEW: Zoom controls - Zoom in/out, set zoom level, enable/disable zoom.
- NEW: Enhanced cookie management - Get/set individual cookies, check for cookies.
- NEW: Advanced scroll controls - Scroll to position, scroll by offset, smooth scrolling.
- NEW: Find in page - Search text, navigate matches, highlight results.
- NEW: File handling - Download files with progress, upload files, file picker integration.
- NEW: Lifecycle management - Proper disposal of resources.
- NEW: JavaScript channel management - Add/remove custom channels dynamically.
- NEW: Console message capture - Capture and monitor console logs.
- NEW: Page metadata extraction - Get page info, links, images, HTML, text.
- NEW: Element interaction - Click elements, set/get input values, inject CSS.
- NEW: Security & URL filtering - Whitelist/blacklist URLs, custom validators.
- NEW: Performance monitoring - Get page load metrics, memory usage, resource counts.
- NEW: Network monitoring - Monitor XHR and Fetch requests.
- NEW: Permission handling - Request and check geolocation, camera, microphone, storage permissions.
- Cross-platform support with minimal dependencies.
Platform Support
| Platform | Support Status | Notes |
|---|---|---|
| Android | Fully supported | Requires minSdkVersion 19 and INTERNET permission. |
| iOS | Fully supported | No additional configuration required. |
| macOS | Fully supported | Background color uses CSS fallback. |
| Web | Fully supported | Uses IFrameElement (built-in). |
| Windows | Fully supported | Uses webview_windows package. |
| Linux | Fully supported | Uses WebKitGTK. |
Installation
Add the following to your pubspec.yaml:
dependencies:
flutter_webview_communication: ^0.1.6
Run flutter pub get to install the package.
Platform Setup
- Android: Ensure the minimum SDK version is set in
android/app/build.gradle:
Add theandroid { defaultConfig { minSdkVersion 19 } }INTERNETpermission inandroid/app/src/main/AndroidManifest.xml:<uses-permission android:name="android.permission.INTERNET" /> - iOS: No additional configuration required.
- macOS: Ensure macOS is enabled in your Flutter project (e.g., via
flutter create --platforms=macos).
Usage
Basic Example (HTML Content with Local Storage)
This example loads HTML content, handles messages, manages local storage, retrieves scroll position, and monitors loading states.
import 'package:flutter/material.dart';
import 'package:flutter_webview_communication/flutter_webview_communication.dart';
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late WebViewPlugin webViewPlugin;
String? errorMessage;
String loadingState = 'idle';
int? loadingProgress;
@override
void initState() {
super.initState();
try {
webViewPlugin = WebViewPlugin(
enableCommunication: true,
actionHandlers: {
'update': (payload) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Received: ${payload['text']}')),
);
},
},
onJavaScriptError: (error) {
setState(() {
errorMessage = error;
});
},
);
} catch (e) {
setState(() {
errorMessage = e.toString();
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: errorMessage != null
? Center(child: Text(errorMessage!))
: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Loading: $loadingState${loadingProgress != null ? ' ($loadingProgress%)' : ''}'),
),
Expanded(
child: webViewPlugin.buildWebView(
content: '''
<h1>Hello</h1>
<button onclick="sendToFlutter('update', {text: 'Hi'})">Click</button>
''',
cssContent: '<style>h1 { color: blue; }</style>',
scriptContent: '''
window.addEventListener('flutterData', (e) => {
document.querySelector('h1').innerText = e.detail.payload.text;
});
''',
csp: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",
userAgent: 'MyApp/1.0',
onLoadingStateChanged: (state, progress, error) {
setState(() {
loadingState = state;
loadingProgress = progress;
if (error != null) {
errorMessage = error;
}
});
},
),
),
],
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: errorMessage != null
? null
: () => webViewPlugin.sendToWebView(
action: 'update',
payload: {'text': 'Hello from Flutter'},
),
child: const Icon(Icons.send),
tooltip: 'Send to WebView',
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: errorMessage != null
? null
: () => webViewPlugin.saveToLocalStorage(
key: 'userData',
value: 'Saved from Flutter',
),
child: const Icon(Icons.save),
tooltip: 'Save to Local Storage',
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: errorMessage != null
? null
: () => webViewPlugin.removeFromLocalStorage(key: 'userData'),
child: const Icon(Icons.delete),
tooltip: 'Remove from Local Storage',
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: errorMessage != null
? null
: () async {
final storage = await webViewPlugin.getLocalStorage();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Storage: $storage')),
);
}
},
child: const Icon(Icons.storage),
tooltip: 'Get Local Storage',
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: errorMessage != null
? null
: () async {
final position = await webViewPlugin.getScrollPosition();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Scroll: x=${position['x']}, y=${position['y']}')),
);
}
},
child: const Icon(Icons.arrow_downward),
tooltip: 'Get Scroll Position',
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: errorMessage != null
? null
: () => webViewPlugin.clearWebViewData(),
child: const Icon(Icons.clear),
tooltip: 'Clear WebView Data',
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: errorMessage != null
? null
: () => webViewPlugin.reload(),
child: const Icon(Icons.refresh),
tooltip: 'Reload WebView',
),
],
),
);
}
}
URL Example with JavaScript Injection
This example loads a URL and injects JavaScript.
import 'package:flutter/material.dart';
import 'package:flutter_webview_communication/flutter_webview_communication.dart';
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late WebViewPlugin webViewPlugin;
String? errorMessage;
String loadingState = 'idle';
int? loadingProgress;
@override
void initState() {
super.initState();
try {
webViewPlugin = WebViewPlugin(
enableCommunication: true,
actionHandlers: {
'update': (payload) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Received: ${payload['text']}')),
);
},
},
onJavaScriptError: (error) {
setState(() {
errorMessage = error;
});
},
);
} catch (e) {
setState(() {
errorMessage = e.toString();
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: errorMessage != null
? Center(child: Text(errorMessage!))
: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Loading: $loadingState${loadingProgress != null ? ' ($loadingProgress%)' : ''}'),
),
Expanded(
child: webViewPlugin.buildWebView(
content: 'https://example.com',
isUrl: true,
scriptContent: '''
window.addEventListener('flutterData', (e) => {
const p = document.createElement('p');
p.textContent = e.detail.payload.text;
document.body.appendChild(p);
});
document.addEventListener('DOMContentLoaded', () => {
const btn = document.createElement('button');
btn.textContent = 'Send to Flutter';
btn.onclick = () => sendToFlutter('update', {text: 'From Web'});
document.body.appendChild(btn);
});
''',
userAgent: 'MyApp/1.0',
onLoadingStateChanged: (state, progress, error) {
setState(() {
loadingState = state;
loadingProgress = progress;
if (error != null) {
errorMessage = error;
}
});
},
),
),
],
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: errorMessage != null
? null
: () => webViewPlugin.sendToWebView(
action: 'update',
payload: {'text': 'Hello from Flutter'},
),
child: const Icon(Icons.send),
tooltip: 'Send to WebView',
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: errorMessage != null
? null
: () => webViewPlugin.saveToLocalStorage(
key: 'userData',
value: 'Saved at ${DateTime.now()}',
),
child: const Icon(Icons.save),
tooltip: 'Save to Local Storage',
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: errorMessage != null
? null
: () => webViewPlugin.removeFromLocalStorage(key: 'userData'),
child: const Icon(Icons.delete),
tooltip: 'Remove from Local Storage',
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: errorMessage != null
? null
: () async {
final storage = await webViewPlugin.getLocalStorage();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Storage: $storage')),
);
}
},
child: const Icon(Icons.storage),
tooltip: 'Get Local Storage',
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: errorMessage != null
? null
: () async {
final position = await webViewPlugin.getScrollPosition();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Scroll: x=${position['x']}, y=${position['y']}')),
);
}
},
child: const Icon(Icons.arrow_downward),
tooltip: 'Get Scroll Position',
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: errorMessage != null
? null
: () => webViewPlugin.clearWebViewData(),
child: const Icon(Icons.clear),
tooltip: 'Clear WebView Data',
),
const SizedBox(height: 10),
FloatingActionButton(
onPressed: errorMessage != null
? null
: () => webViewPlugin.reload(),
child: const Icon(Icons.refresh),
tooltip: 'Reload WebView',
),
],
),
);
}
}
Simple Example
A minimal setup for quick integration.
import 'package:flutter_webview_communication/flutter_webview_communication.dart';
final plugin = WebViewPlugin(
enableCommunication: true,
actionHandlers: {
'update': (payload) => debugPrint('Received: $payload'),
},
onJavaScriptError: (error) => debugPrint('JS Error: $error'),
);
Widget webView = plugin.buildWebView(
content: '<h1>Hello</h1>',
cssContent: '<style>h1 { color: blue; }</style>',
scriptContent: 'console.log("Loaded");',
csp: "default-src 'self'; script-src 'self' 'unsafe-inline'",
userAgent: 'MyApp/1.0',
onLoadingStateChanged: (state, progress, error) => debugPrint('$state: $error'),
);
plugin.sendToWebView(action: 'update', payload: {'text': 'Hello'});
plugin.saveToLocalStorage(key: 'test', value: 'Simple data');
plugin.getLocalStorage().then((data) => debugPrint('Storage: $data'));
plugin.removeFromLocalStorage(key: 'test');
plugin.clearWebViewData();
plugin.getScrollPosition().then((pos) => debugPrint('Scroll: x=${pos['x']}, y=${pos['y']}'));
plugin.reload();
API
WebViewPlugin
WebViewPlugin({Map<String, Function(Map<String, dynamic>)?>? actionHandlers, bool enableCommunication = false, Function(String)? onJavaScriptError})- Initializes the plugin with optional action handlers, communication script injection, and JavaScript error callback. Throws
Exceptionon unsupported platforms (e.g., Web, Windows, Linux). - Parameters:
actionHandlers: Map of action names to handler functions for WebView messages.enableCommunication: Enables injection ofsendToFlutterandreceiveFromFlutterscripts (default:false).onJavaScriptError: Callback for JavaScript errors.
- Initializes the plugin with optional action handlers, communication script injection, and JavaScript error callback. Throws
buildWebView({required String content, bool isUrl = false, String? cssContent, String? scriptContent, double? height, double? width, Color? backgroundColor, bool? enableCommunication, String? userAgent, String? csp, Function(String state, int? progress, String? error)? onLoadingStateChanged})- Creates a WebView widget with the specified content (HTML or URL).
- Parameters:
content: HTML string or URL.isUrl: Treatcontentas a URL iftrue(default:false).cssContent: CSS to include in<head>(HTML only).scriptContent: JavaScript to embed (HTML) or inject post-load (URL).height,width: Optional dimensions for the WebView.backgroundColor: Background color (not supported on macOS).enableCommunication: Overrides constructor’s setting for communication scripts.userAgent: Custom user agent string.csp: Content Security Policy for HTML content.onLoadingStateChanged: Callback for loading states (started,progress,finished,error) with progress and error details.
sendToWebView({required dynamic payload, String? action})- Sends JSON-serializable data to the WebView with an optional action identifier.
saveToLocalStorage({required String key, required dynamic value})- Saves a key-value pair to the WebView’s local storage. The value is JSON-stringified.
removeFromLocalStorage({required String key})- Removes a key from the WebView’s local storage.
getLocalStorage()- Retrieves all key-value pairs from local storage, with JSON-decoded values where possible.
clearWebViewData()- Clears cookies, cache, and local storage.
reload()- Reloads the current WebView content (HTML or URL).
getScrollPosition()- Returns the current scroll position as a
Map<String, double>withxandycoordinates, using JavaScript.
- Returns the current scroll position as a
dispose()- Disposes of the WebView and cleans up resources. Call this when the WebView is no longer needed.
Navigation Controls
goBack()- Navigates back in the WebView history. Returns
trueif successful.
- Navigates back in the WebView history. Returns
goForward()- Navigates forward in the WebView history. Returns
trueif successful.
- Navigates forward in the WebView history. Returns
canGoBack()- Checks if the WebView can navigate back. Returns
bool.
- Checks if the WebView can navigate back. Returns
canGoForward()- Checks if the WebView can navigate forward. Returns
bool.
- Checks if the WebView can navigate forward. Returns
getCurrentUrl()- Gets the current URL of the WebView. Returns
String?.
- Gets the current URL of the WebView. Returns
getTitle()- Gets the title of the current page. Returns
String?.
- Gets the title of the current page. Returns
stopLoading()- Stops the current page load.
Zoom Controls
setZoomEnabled(bool enabled)- Enables or disables zoom functionality.
zoomIn()- Zooms in by 20%.
zoomOut()- Zooms out by 20%.
setZoomLevel(double level)- Sets the zoom level (1.0 = 100%, range: 0.5-5.0).
getZoomLevel()- Gets the current zoom level. Returns
double.
- Gets the current zoom level. Returns
Cookie Management
getCookie(String name, String url)- Gets a specific cookie value. Returns
String?.
- Gets a specific cookie value. Returns
setCookie({required String name, required String value, required String domain, String path = '/', DateTime? expiresDate})- Sets a cookie with optional expiration.
hasCookies()- Checks if any cookies exist. Returns
bool.
- Checks if any cookies exist. Returns
getAllCookies()- Gets all cookies as a
Map<String, String>.
- Gets all cookies as a
Scroll Controls
scrollTo(double x, double y, {bool smooth = false})- Scrolls to a specific position.
scrollBy(double x, double y, {bool smooth = false})- Scrolls by a specific offset.
scrollToTop({bool smooth = true})- Scrolls to the top of the page.
scrollToBottom({bool smooth = true})- Scrolls to the bottom of the page.
Find in Page
findInPage(String searchText, {bool caseSensitive = false})- Finds all occurrences of text in the page. Returns match count.
findNext()- Navigates to the next match.
findPrevious()- Navigates to the previous match.
clearFindMatches()- Clears all find highlights.
getFindMatchInfo()- Gets current match information as
Map<String, int>with 'current' and 'total' keys.
- Gets current match information as
File Handling
downloadFile(String url, String savePath, {OnDownloadProgress? onProgress, String? filename})- Downloads a file from URL to specified path with progress tracking. Returns
String(full path).
- Downloads a file from URL to specified path with progress tracking. Returns
cancelDownload(String url)- Cancels an active download. Returns
bool.
- Cancels an active download. Returns
getActiveDownloads()- Gets list of currently active download URLs. Returns
List<String>.
- Gets list of currently active download URLs. Returns
pickFiles({bool allowMultiple = false, List<String>? acceptTypes})- Opens file picker for file selection. Returns
List<String>(file paths).
- Opens file picker for file selection. Returns
getDownloadsDirectoryPath()- Gets platform-specific downloads directory path. Returns
String?.
- Gets platform-specific downloads directory path. Returns
handleFileDownload(String url, String suggestedFilename, String? mimeType)- Triggers the file download callback if set.
handleFileUpload({bool allowMultiple = false, List<String>? acceptTypes})- Triggers the file upload callback or default picker. Returns
List<String>.
- Triggers the file upload callback or default picker. Returns
getMimeType(String filename)- Gets MIME type from filename extension. Returns
String.
- Gets MIME type from filename extension. Returns
Callbacks:
OnFileDownload- Callback for file download requests:void Function(String url, String suggestedFilename, String? mimeType)OnFileUploadRequest- Callback for file upload requests:Future<List<String>> Function(bool allowMultiple, List<String>? acceptTypes)OnDownloadProgress- Callback for download progress:void Function(int received, int total)
JavaScript Channel Management
addJavaScriptChannel(String name, Function callback)- Adds a custom JavaScript channel for communication.
removeJavaScriptChannel(String name)- Removes a JavaScript channel. Returns
bool.
- Removes a JavaScript channel. Returns
listJavaScriptChannels()- Lists all registered channel names. Returns
List<String>.
- Lists all registered channel names. Returns
hasJavaScriptChannel(String name)- Checks if a channel exists. Returns
bool.
- Checks if a channel exists. Returns
Console Message Capture
enableConsoleCapture()- Enables capturing of console.log, console.error, console.warn, console.info.
getConsoleMessages()- Gets all captured console messages. Returns
List<String>.
- Gets all captured console messages. Returns
clearConsoleMessages()- Clears the console message history.
Page Metadata & Information
getPageMetadata()- Gets comprehensive page metadata (url, title, description, keywords, author, viewport, charset). Returns
Map<String, String?>.
- Gets comprehensive page metadata (url, title, description, keywords, author, viewport, charset). Returns
getPageLinks()- Gets all links on the page. Returns
List<Map<String, String>>with 'href' and 'text'.
- Gets all links on the page. Returns
getPageImages()- Gets all images on the page. Returns
List<Map<String, String>>with 'src', 'alt', 'width', 'height'.
- Gets all images on the page. Returns
getPageHtml({bool includeHead = false})- Gets the page's HTML content. Returns
String.
- Gets the page's HTML content. Returns
getPageText()- Gets the page's text content (without HTML tags). Returns
String.
- Gets the page's text content (without HTML tags). Returns
Element Interaction
injectCSS(String css, {String? id})- Injects custom CSS into the page.
removeInjectedCSS(String id)- Removes injected CSS by ID.
clickElement(String selector)- Clicks an element by CSS selector. Returns
bool.
- Clicks an element by CSS selector. Returns
setInputValue(String selector, String value)- Sets the value of an input element. Returns
bool.
- Sets the value of an input element. Returns
getInputValue(String selector)- Gets the value of an input element. Returns
String?.
- Gets the value of an input element. Returns
elementExists(String selector)- Checks if an element exists. Returns
bool.
- Checks if an element exists. Returns
countElements(String selector)- Counts elements matching a selector. Returns
int.
- Counts elements matching a selector. Returns
scrollElementIntoView(String selector, {bool smooth = true})- Scrolls an element into view. Returns
bool.
- Scrolls an element into view. Returns
Security & URL Filtering
setAllowedUrls(List<String> urls)- Sets URL whitelist. Supports wildcards and regex patterns.
setBlockedUrls(List<String> urls)- Sets URL blacklist. Supports wildcards and regex patterns.
setUrlValidator(bool Function(String) validator)- Sets custom URL validator function.
isUrlAllowed(String url)- Checks if a URL is allowed. Returns
bool.
- Checks if a URL is allowed. Returns
clearUrlRestrictions()- Clears all URL restrictions.
Performance Monitoring
getPerformanceMetrics()- Gets comprehensive page load performance metrics. Returns
Map<String, dynamic>.
- Gets comprehensive page load performance metrics. Returns
getMemoryUsage()- Gets JavaScript memory usage information. Returns
Map<String, int>?.
- Gets JavaScript memory usage information. Returns
getResourceCount()- Gets count of loaded resources by type. Returns
Map<String, int>.
- Gets count of loaded resources by type. Returns
Network Monitoring
enableNetworkMonitoring()- Enables monitoring of XHR and Fetch requests.
JavaScript API
sendToFlutter(action, payload)- Sends a message to Flutter with an action string and payload object.
- Example:
sendToFlutter("update", { text: "Hello from Web" });
receiveFromFlutter(data)- Handles data from Flutter, dispatched as a
flutterDataevent. - Example:
window.addEventListener("flutterData", (e) => { console.log(e.detail); // {action: 'update', payload: {text: 'Hello'}} });
- Handles data from Flutter, dispatched as a
Platform-Specific Features
For advanced features like auto-playing media, add platform-specific dependencies to pubspec.yaml:
dependencies:
webview_flutter: ^4.8.0
webview_flutter_android: ^3.16.4
webview_flutter_wkwebview: ^3.14.0
Configure platform-specific settings:
import 'package:webview_flutter_android/webview_flutter_android.dart';
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
void configureWebView(WebViewPlugin plugin) {
final controller = plugin.buildWebView(content: 'https://example.com', isUrl: true);
if (controller.platform is AndroidWebViewController) {
(controller.platform as AndroidWebViewController).setMediaPlaybackRequiresUserGesture(false);
} else if (controller.platform is WebKitWebViewController) {
(controller.platform as WebKitWebViewController).setAllowsInlineMediaPlayback(true);
}
}
Debugging Tips
- Local Storage: Use WebView developer tools (if available) to inspect
localStorageand verify saved, retrieved, or cleared data. - Loading States: Monitor
onLoadingStateChangedoutputs to track progress percentages and errors (e.g., HTTP or resource errors). - Scroll Position: Verify
getScrollPositionreturns accuratex/ycoordinates after scrolling. - JavaScript Errors: Check
onJavaScriptErrorlogs for issues in injected scripts or communication. - Logs: Review
debugPrintoutputs in the Flutter console for operation success or errors. - Platform Issues: Ensure Android
INTERNETpermission is set andminSdkVersionis 19 or higher.
Contributing
Contributions are welcome! Please submit issues or pull requests to the GitHub repository.
Libraries
- flutter_webview_communication
- Flutter WebView Communication Plugin