[webview_flutter] Copies app-facing implementation of webview_flutter from v4_webview (#6856)
* copy code from v4_webview
* version bump and readme update
* work towards better readme
* improvements
* more readme progress
* improvements
* fix main and update more readme
* excerpt changes and more 3.0 diffs
* cookie manager update
* remove packages from exclude list
* lint
* better range
* isForMainFrame
* load page after waiting for widget
* fix integration tests
* improve readme a bit
* collapse changelong. update platform-specific wording. include in excerpt tests
* use platform implementation packages
* include missing exports
* PR comments
* correct spelling
* interface dev dependency
* move other usage above migration
* remove interface classes
diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md
index f278cf9..329ce48 100644
--- a/packages/webview_flutter/webview_flutter/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md
@@ -1,11 +1,11 @@
-## NEXT
+## 4.0.0
-* Updates code for `no_leading_underscores_for_local_identifiers` lint.
-* Updates minimum Flutter version to 2.10.
-* Fixes avoid_redundant_argument_values lint warnings and minor typos.
-* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231).
+* **BREAKING CHANGE** Updates implementation to use the `2.0.0` release of
+ `webview_flutter_platform_interface`. See `Usage` section in the README for updated usage. See
+ `Migrating from 3.0 to 4.0` section in the README for details on migrating to this version.
+* Updates minimum Flutter version to 3.0.0.
+* Updates code for new analysis options.
* Updates references to the obsolete master branch.
-* Fixes typo from lowercase to uppercase.
## 3.0.4
diff --git a/packages/webview_flutter/webview_flutter/README.md b/packages/webview_flutter/webview_flutter/README.md
index ffe9144..98f7b66 100644
--- a/packages/webview_flutter/webview_flutter/README.md
+++ b/packages/webview_flutter/webview_flutter/README.md
@@ -1,10 +1,12 @@
# WebView for Flutter
+<?code-excerpt path-base="excerpts/packages/webview_flutter_example"?>
+
[![pub package](https://img.shields.io/pub/v/webview_flutter.svg)](https://pub.dev/packages/webview_flutter)
A Flutter plugin that provides a WebView widget.
-On iOS the WebView widget is backed by a [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview);
+On iOS the WebView widget is backed by a [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview).
On Android the WebView widget is backed by a [WebView](https://developer.android.com/reference/android/webkit/WebView).
| | Android | iOS |
@@ -12,30 +14,60 @@
| **Support** | SDK 19+ or 20+ | 9.0+ |
## Usage
-Add `webview_flutter` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). If you are targeting Android, make sure to read the *Android Platform Views* section below to choose the platform view mode that best suits your needs.
+Add `webview_flutter` as a [dependency in your pubspec.yaml file](https://pub.dev/packages/webview_flutter/install).
-You can now include a WebView widget in your widget tree. See the
-[WebView](https://pub.dev/documentation/webview_flutter/latest/webview_flutter/WebView-class.html)
-widget's Dartdoc for more details on how to use the widget.
+You can now display a WebView by:
-## Android Platform Views
+1. Instantiating a [WebViewController](https://pub.dev/documentation/webview_flutter/latest/webview_flutter/WebViewController-class.html).
+
+<?code-excerpt "simple_example.dart (webview_controller)"?>
+```dart
+controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setBackgroundColor(const Color(0x00000000))
+ ..setNavigationDelegate(
+ NavigationDelegate(
+ onProgress: (int progress) {
+ // Update loading bar.
+ },
+ onPageStarted: (String url) {},
+ onPageFinished: (String url) {},
+ onWebResourceError: (WebResourceError error) {},
+ onNavigationRequest: (NavigationRequest request) {
+ if (request.url.startsWith('https://www.youtube.com/')) {
+ return NavigationDecision.prevent;
+ }
+ return NavigationDecision.navigate;
+ },
+ ),
+ )
+ ..loadRequest(Uri.parse('https://flutter.dev'));
+```
+
+2. Passing the controller to a [WebViewWidget](https://pub.dev/documentation/webview_flutter/latest/webview_flutter/WebViewWidget-class.html).
+
+<?code-excerpt "simple_example.dart (webview_widget)"?>
+```dart
+@override
+Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: const Text('Flutter Simple Example')),
+ body: WebViewWidget(controller: controller),
+ );
+}
+```
+
+See the Dartdocs for [WebViewController](https://pub.dev/documentation/webview_flutter/latest/webview_flutter/WebViewController-class.html)
+and [WebViewWidget](https://pub.dev/documentation/webview_flutter/latest/webview_flutter/WebViewWidget-class.html)
+for more details.
+
+### Android Platform Views
+
This plugin uses
[Platform Views](https://flutter.dev/docs/development/platform-integration/platform-views) to embed
-the Android’s webview within the Flutter app. It supports two modes:
-*hybrid composition* (the current default) and *virtual display*.
+the Android’s WebView within the Flutter app.
-Here are some points to consider when choosing between the two:
-
-* *Hybrid composition* has built-in keyboard support while *virtual display* has multiple
-[keyboard issues](https://github.com/flutter/flutter/issues?q=is%3Aopen+label%3Avd-only+label%3A%22p%3A+webview-keyboard%22).
-* *Hybrid composition* requires Android SDK 19+ while *virtual display* requires Android SDK 20+.
-* *Hybrid composition* and *virtual display* have different
- [performance tradeoffs](https://flutter.dev/docs/development/platform-integration/platform-views#performance).
-
-
-### Using Hybrid Composition
-
-The mode is currently enabled by default. You should however make sure to set the correct `minSdkVersion` in `android/app/build.gradle` if it was previously lower than 19:
+You should however make sure to set the correct `minSdkVersion` in `android/app/build.gradle` if it was previously lower than 19:
```groovy
android {
@@ -45,47 +77,67 @@
}
```
-### Using Virtual displays
+### Platform-Specific Features
-1. Set the correct `minSdkVersion` in `android/app/build.gradle` (if it was previously lower than 20):
+Many classes have a subclass or an underlying implementation that provides access to platform-specific
+features.
- ```groovy
- android {
- defaultConfig {
- minSdkVersion 20
- }
- }
- ```
+To access platform-specific features, start by adding the platform implementation packages to your
+app or package:
-2. Set `WebView.platform = AndroidWebView();` in `initState()`.
- For example:
+* **Android**: [webview_flutter_android](https://pub.dev/packages/webview_flutter_android/install)
+* **iOS**: [webview_flutter_wkwebview](https://pub.dev/packages/webview_flutter_wkwebview/install)
- ```dart
- import 'dart:io';
+Next, add the imports of the implementation packages to your app or package:
- import 'package:webview_flutter/webview_flutter.dart';
+<?code-excerpt "main.dart (platform_imports)"?>
+```dart
+// Import for Android features.
+import 'package:webview_flutter_android/webview_flutter_android.dart';
+// Import for iOS features.
+import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
+```
- class WebViewExample extends StatefulWidget {
- @override
- WebViewExampleState createState() => WebViewExampleState();
- }
+Now, additional features can be accessed through the platform implementations. Classes
+`WebViewController`, `WebViewWidget`, `NavigationDelegate`, and `WebViewCookieManager` pass their
+functionality to a class provided by the current platform. Below are a couple of ways to access
+additional functionality provided by the platform and is followed by an example.
- class WebViewExampleState extends State<WebViewExample> {
- @override
- void initState() {
- super.initState();
- // Enable virtual display.
- if (Platform.isAndroid) WebView.platform = AndroidWebView();
- }
+1. Pass a creation params class provided by a platform implementation to a `fromPlatformCreationParams`
+ constructor (e.g. `WebViewController.fromPlatformCreationParams`,
+ `WebViewWidget.fromPlatformCreationParams`, etc.).
+2. Call methods on a platform implementation of a class by using the `platform` field (e.g.
+ `WebViewController.platform`, `WebViewWidget.platform`, etc.).
- @override
- Widget build(BuildContext context) {
- return WebView(
- initialUrl: 'https://flutter.dev',
- );
- }
- }
- ```
+Below is an example of setting additional iOS and Android parameters on the `WebViewController`.
+
+<?code-excerpt "main.dart (platform_features)"?>
+```dart
+late final PlatformWebViewControllerCreationParams params;
+if (WebViewPlatform.instance is WebKitWebViewPlatform) {
+ params = WebKitWebViewControllerCreationParams(
+ allowsInlineMediaPlayback: true,
+ mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
+ );
+} else {
+ params = const PlatformWebViewControllerCreationParams();
+}
+
+final WebViewController controller =
+ WebViewController.fromPlatformCreationParams(params);
+// ···
+if (controller.platform is AndroidWebViewController) {
+ AndroidWebViewController.enableDebugging(true);
+ (controller.platform as AndroidWebViewController)
+ .setMediaPlaybackRequiresUserGesture(false);
+}
+```
+
+See https://pub.dev/documentation/webview_flutter_android/latest/webview_flutter_android/webview_flutter_android-library.html
+for more details on Android features.
+
+See https://pub.dev/documentation/webview_flutter_wkwebview/latest/webview_flutter_wkwebview/webview_flutter_wkwebview-library.html
+for more details on iOS features.
### Enable Material Components for Android
@@ -95,4 +147,76 @@
### Setting custom headers on POST requests
Currently, setting custom headers when making a post request with the WebViewController's `loadRequest` method is not supported on Android.
-If you require this functionality, a workaround is to make the request manually, and then load the response data using `loadHTMLString` instead.
+If you require this functionality, a workaround is to make the request manually, and then load the response data using `loadHtmlString` instead.
+
+## Migrating from 3.0 to 4.0
+
+### Instantiating WebViewController
+
+In version 3.0 and below, `WebViewController` could only be retrieved in a callback after the
+`WebView` was added to the widget tree. Now, `WebViewController` must be instantiated and can be
+used before it is added to the widget tree. See `Usage` section above for an example.
+
+### Replacing WebView Functionality
+
+The `WebView` class has been removed and its functionality has been split into `WebViewController`
+and `WebViewWidget`.
+
+`WebViewController` handles all functionality that is associated with the underlying web view
+provided by each platform. (e.g., loading a url, setting the background color of the underlying
+platform view, or clearing the cache).
+
+`WebViewWidget` takes a `WebViewController` and handles all Flutter widget related functionality
+(e.g., layout direction, gesture recognizers).
+
+See the Dartdocs for [WebViewController](https://pub.dev/documentation/webview_flutter/latest/webview_flutter/WebViewController-class.html)
+and [WebViewWidget](https://pub.dev/documentation/webview_flutter/latest/webview_flutter/WebViewWidget-class.html)
+for more details.
+
+### PlatformView Implementation on Android
+
+The PlatformView implementation for Android is currently no longer configurable. It uses Texture
+Layer Hybrid Composition on versions 23+ and automatically fallbacks to Hybrid Composition for
+version 19-23. See https://github.com/flutter/flutter/issues/108106 for progress on manually
+switching to Hybrid Composition on versions 23+.
+
+### API Changes
+
+Below is a non-exhaustive list of changes to the API:
+
+* `WebViewController.clearCache` no longer clears local storage. Please use
+ `WebViewController.clearLocalStorage`.
+* `WebViewController.clearCache` no longer reloads the page.
+* `WebViewController.loadUrl` has been removed. Please use `WebViewController.loadRequest`.
+* `WebViewController.evaluateJavascript` has been removed. Please use
+ `WebViewController.runJavaScript` or `WebViewController.runJavaScriptReturningResult`.
+* `WebViewController.getScrollX` and `WebViewController.getScrollY` have been removed and have
+ been replaced by `WebViewController.getScrollPosition`.
+* `WebViewController.runJavaScriptReturningResult` now returns an `Object` and not a `String`. This
+ will attempt to return a `bool` or `num` if the return value can be parsed.
+* `CookieManager` is replaced by `WebViewCookieManager`.
+* `NavigationDelegate.onWebResourceError` callback includes errors that are not from the main frame.
+ Use the `WebResourceError.isForMainFrame` field to filter errors.
+* The following fields from `WebView` have been moved to `NavigationDelegate`. They can be added to
+ a WebView with `WebViewController.setNavigationDelegate`.
+ * `WebView.navigationDelegate` -> `NavigationDelegate.onNavigationRequest`
+ * `WebView.onPageStarted` -> `NavigationDelegate.onPageStarted`
+ * `WebView.onPageFinished` -> `NavigationDelegate.onPageFinished`
+ * `WebView.onProgress` -> `NavigationDelegate.onProgress`
+ * `WebView.onWebResourceError` -> `NavigationDelegate.onWebResourceError`
+* The following fields from `WebView` have been moved to `WebViewController`:
+ * `WebView.javascriptMode` -> `WebViewController.setJavaScriptMode`
+ * `WebView.javascriptChannels` ->
+ `WebViewController.addJavaScriptChannel`/`WebViewController.removeJavaScriptChannel`
+ * `WebView.zoomEnabled` -> `WebViewController.enableZoom`
+ * `WebView.userAgent` -> `WebViewController.setUserAgent`
+ * `WebView.backgroundColor` -> `WebViewController.setBackgroundColor`
+* The following features have been moved to an Android implementation class. See section
+ `Platform-Specific Features` for details on accessing Android platform specific features.
+ * `WebView.debuggingEnabled` -> `static AndroidWebViewController.enableDebugging`
+ * `WebView.initialMediaPlaybackPolicy` -> `AndroidWebViewController.setMediaPlaybackRequiresUserGesture`
+* The following features have been moved to an iOS implementation class. See section
+ `Platform-Specific Features` for details on accessing iOS platform specific features.
+ * `WebView.gestureNavigationEnabled` -> `WebKitWebViewController.setAllowsBackForwardNavigationGestures`
+ * `WebView.initialMediaPlaybackPolicy` -> `WebKitWebViewControllerCreationParams.mediaTypesRequiringUserAction`
+ * `WebView.allowsInlineMediaPlayback` -> `WebKitWebViewControllerCreationParams.allowsInlineMediaPlayback`
diff --git a/packages/webview_flutter/webview_flutter/example/build.excerpt.yaml b/packages/webview_flutter/webview_flutter/example/build.excerpt.yaml
new file mode 100644
index 0000000..46c1e75
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter/example/build.excerpt.yaml
@@ -0,0 +1,15 @@
+targets:
+ $default:
+ sources:
+ include:
+ - lib/**
+ # Some default includes that aren't really used here but will prevent
+ # false-negative warnings:
+ - $package$
+ - lib/$lib$
+ exclude:
+ - '**/.*/**'
+ - '**/build/**'
+ builders:
+ code_excerpter|code_excerpter:
+ enabled: true
\ No newline at end of file
diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/legacy/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/legacy/webview_flutter_test.dart
new file mode 100644
index 0000000..1453910
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter/example/integration_test/legacy/webview_flutter_test.dart
@@ -0,0 +1,1382 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This test is run using `flutter drive` by the CI (see /script/tool/README.md
+// in this repository for details on driving that tooling manually), but can
+// also be run using `flutter test` directly during development.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231)
+// ignore: unnecessary_import
+import 'dart:typed_data';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:integration_test/integration_test.dart';
+import 'package:webview_flutter/src/webview_flutter_legacy.dart';
+
+Future<void> main() async {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+
+ final HttpServer server = await HttpServer.bind(InternetAddress.anyIPv4, 0);
+ server.forEach((HttpRequest request) {
+ if (request.uri.path == '/hello.txt') {
+ request.response.writeln('Hello, world.');
+ } else if (request.uri.path == '/secondary.txt') {
+ request.response.writeln('How are you today?');
+ } else if (request.uri.path == '/headers') {
+ request.response.writeln('${request.headers}');
+ } else if (request.uri.path == '/favicon.ico') {
+ request.response.statusCode = HttpStatus.notFound;
+ } else {
+ fail('unexpected request: ${request.method} ${request.uri}');
+ }
+ request.response.close();
+ });
+ final String prefixUrl = 'http://${server.address.address}:${server.port}';
+ final String primaryUrl = '$prefixUrl/hello.txt';
+ final String secondaryUrl = '$prefixUrl/secondary.txt';
+ final String headersUrl = '$prefixUrl/headers';
+
+ testWidgets('initialUrl', (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ final Completer<void> pageFinishedCompleter = Completer<void>();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: primaryUrl,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ onPageFinished: pageFinishedCompleter.complete,
+ ),
+ ),
+ );
+
+ final WebViewController controller = await controllerCompleter.future;
+ await pageFinishedCompleter.future;
+
+ final String? currentUrl = await controller.currentUrl();
+ expect(currentUrl, primaryUrl);
+ });
+
+ testWidgets('loadUrl', (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ final StreamController<String> pageLoads = StreamController<String>();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: primaryUrl,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ onPageFinished: (String url) {
+ pageLoads.add(url);
+ },
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+
+ await controller.loadUrl(secondaryUrl);
+ await expectLater(
+ pageLoads.stream.firstWhere((String url) => url == secondaryUrl),
+ completion(secondaryUrl),
+ );
+ });
+
+ testWidgets('evaluateJavascript', (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: primaryUrl,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+ // ignore: deprecated_member_use
+ final String result = await controller.evaluateJavascript('1 + 1');
+ expect(result, equals('2'));
+ });
+
+ testWidgets('loadUrl with headers', (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ final StreamController<String> pageStarts = StreamController<String>();
+ final StreamController<String> pageLoads = StreamController<String>();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: primaryUrl,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ onPageStarted: (String url) {
+ pageStarts.add(url);
+ },
+ onPageFinished: (String url) {
+ pageLoads.add(url);
+ },
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+ final Map<String, String> headers = <String, String>{
+ 'test_header': 'flutter_test_header'
+ };
+ await controller.loadUrl(headersUrl, headers: headers);
+ final String? currentUrl = await controller.currentUrl();
+ expect(currentUrl, headersUrl);
+
+ await pageStarts.stream.firstWhere((String url) => url == currentUrl);
+ await pageLoads.stream.firstWhere((String url) => url == currentUrl);
+
+ final String content = await controller
+ .runJavascriptReturningResult('document.documentElement.innerText');
+ expect(content.contains('flutter_test_header'), isTrue);
+ });
+
+ testWidgets('JavascriptChannel', (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ final Completer<void> pageStarted = Completer<void>();
+ final Completer<void> pageLoaded = Completer<void>();
+ final Completer<String> channelCompleter = Completer<String>();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ // This is the data URL for: '<!DOCTYPE html>'
+ initialUrl:
+ 'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+',
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ javascriptChannels: <JavascriptChannel>{
+ JavascriptChannel(
+ name: 'Echo',
+ onMessageReceived: (JavascriptMessage message) {
+ channelCompleter.complete(message.message);
+ },
+ ),
+ },
+ onPageStarted: (String url) {
+ pageStarted.complete(null);
+ },
+ onPageFinished: (String url) {
+ pageLoaded.complete(null);
+ },
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+ await pageStarted.future;
+ await pageLoaded.future;
+
+ expect(channelCompleter.isCompleted, isFalse);
+ await controller.runJavascript('Echo.postMessage("hello");');
+
+ await expectLater(channelCompleter.future, completion('hello'));
+ });
+
+ testWidgets('resize webview', (WidgetTester tester) async {
+ final Completer<void> initialResizeCompleter = Completer<void>();
+ final Completer<void> buttonTapResizeCompleter = Completer<void>();
+ final Completer<void> onPageFinished = Completer<void>();
+
+ bool resizeButtonTapped = false;
+ await tester.pumpWidget(ResizableWebView(
+ onResize: (_) {
+ if (resizeButtonTapped) {
+ buttonTapResizeCompleter.complete();
+ } else {
+ initialResizeCompleter.complete();
+ }
+ },
+ onPageFinished: () => onPageFinished.complete(),
+ ));
+ await onPageFinished.future;
+ // Wait for a potential call to resize after page is loaded.
+ await initialResizeCompleter.future.timeout(
+ const Duration(seconds: 3),
+ onTimeout: () => null,
+ );
+
+ resizeButtonTapped = true;
+ await tester.tap(find.byKey(const ValueKey<String>('resizeButton')));
+ await tester.pumpAndSettle();
+ expect(buttonTapResizeCompleter.future, completes);
+ });
+
+ testWidgets('set custom userAgent', (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter1 =
+ Completer<WebViewController>();
+ final GlobalKey globalKey = GlobalKey();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: globalKey,
+ initialUrl: 'about:blank',
+ javascriptMode: JavascriptMode.unrestricted,
+ userAgent: 'Custom_User_Agent1',
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter1.complete(controller);
+ },
+ ),
+ ),
+ );
+ final WebViewController controller1 = await controllerCompleter1.future;
+ final String customUserAgent1 = await _getUserAgent(controller1);
+ expect(customUserAgent1, 'Custom_User_Agent1');
+ // rebuild the WebView with a different user agent.
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: globalKey,
+ initialUrl: 'about:blank',
+ javascriptMode: JavascriptMode.unrestricted,
+ userAgent: 'Custom_User_Agent2',
+ ),
+ ),
+ );
+
+ final String customUserAgent2 = await _getUserAgent(controller1);
+ expect(customUserAgent2, 'Custom_User_Agent2');
+ });
+
+ testWidgets('use default platform userAgent after webView is rebuilt',
+ (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ final GlobalKey globalKey = GlobalKey();
+ // Build the webView with no user agent to get the default platform user agent.
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: globalKey,
+ initialUrl: primaryUrl,
+ javascriptMode: JavascriptMode.unrestricted,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+ final String defaultPlatformUserAgent = await _getUserAgent(controller);
+ // rebuild the WebView with a custom user agent.
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: globalKey,
+ initialUrl: 'about:blank',
+ javascriptMode: JavascriptMode.unrestricted,
+ userAgent: 'Custom_User_Agent',
+ ),
+ ),
+ );
+ final String customUserAgent = await _getUserAgent(controller);
+ expect(customUserAgent, 'Custom_User_Agent');
+ // rebuilds the WebView with no user agent.
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: globalKey,
+ initialUrl: 'about:blank',
+ javascriptMode: JavascriptMode.unrestricted,
+ ),
+ ),
+ );
+
+ final String customUserAgent2 = await _getUserAgent(controller);
+ expect(customUserAgent2, defaultPlatformUserAgent);
+ });
+
+ group('Video playback policy', () {
+ late String videoTestBase64;
+ setUpAll(() async {
+ final ByteData videoData =
+ await rootBundle.load('assets/sample_video.mp4');
+ final String base64VideoData =
+ base64Encode(Uint8List.view(videoData.buffer));
+ final String videoTest = '''
+ <!DOCTYPE html><html>
+ <head><title>Video auto play</title>
+ <script type="text/javascript">
+ function play() {
+ var video = document.getElementById("video");
+ video.play();
+ video.addEventListener('timeupdate', videoTimeUpdateHandler, false);
+ }
+ function videoTimeUpdateHandler(e) {
+ var video = document.getElementById("video");
+ VideoTestTime.postMessage(video.currentTime);
+ }
+ function isPaused() {
+ var video = document.getElementById("video");
+ return video.paused;
+ }
+ function isFullScreen() {
+ var video = document.getElementById("video");
+ return video.webkitDisplayingFullscreen;
+ }
+ </script>
+ </head>
+ <body onload="play();">
+ <video controls playsinline autoplay id="video">
+ <source src="data:video/mp4;charset=utf-8;base64,$base64VideoData">
+ </video>
+ </body>
+ </html>
+ ''';
+ videoTestBase64 = base64Encode(const Utf8Encoder().convert(videoTest));
+ });
+
+ testWidgets('Auto media playback', (WidgetTester tester) async {
+ Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ Completer<void> pageLoaded = Completer<void>();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ onPageFinished: (String url) {
+ pageLoaded.complete(null);
+ },
+ initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
+ ),
+ ),
+ );
+ WebViewController controller = await controllerCompleter.future;
+ await pageLoaded.future;
+
+ String isPaused =
+ await controller.runJavascriptReturningResult('isPaused();');
+ expect(isPaused, _webviewBool(false));
+
+ controllerCompleter = Completer<WebViewController>();
+ pageLoaded = Completer<void>();
+
+ // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ onPageFinished: (String url) {
+ pageLoaded.complete(null);
+ },
+ ),
+ ),
+ );
+
+ controller = await controllerCompleter.future;
+ await pageLoaded.future;
+
+ isPaused = await controller.runJavascriptReturningResult('isPaused();');
+ expect(isPaused, _webviewBool(true));
+ });
+
+ testWidgets('Changes to initialMediaPlaybackPolicy are ignored',
+ (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ Completer<void> pageLoaded = Completer<void>();
+
+ final GlobalKey key = GlobalKey();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: key,
+ initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ onPageFinished: (String url) {
+ pageLoaded.complete(null);
+ },
+ initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+ await pageLoaded.future;
+
+ String isPaused =
+ await controller.runJavascriptReturningResult('isPaused();');
+ expect(isPaused, _webviewBool(false));
+
+ pageLoaded = Completer<void>();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: key,
+ initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ onPageFinished: (String url) {
+ pageLoaded.complete(null);
+ },
+ ),
+ ),
+ );
+
+ await controller.reload();
+
+ await pageLoaded.future;
+
+ isPaused = await controller.runJavascriptReturningResult('isPaused();');
+ expect(isPaused, _webviewBool(false));
+ });
+
+ testWidgets('Video plays inline when allowsInlineMediaPlayback is true',
+ (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ final Completer<void> pageLoaded = Completer<void>();
+ final Completer<void> videoPlaying = Completer<void>();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ javascriptChannels: <JavascriptChannel>{
+ JavascriptChannel(
+ name: 'VideoTestTime',
+ onMessageReceived: (JavascriptMessage message) {
+ final double currentTime = double.parse(message.message);
+ // Let it play for at least 1 second to make sure the related video's properties are set.
+ if (currentTime > 1 && !videoPlaying.isCompleted) {
+ videoPlaying.complete(null);
+ }
+ },
+ ),
+ },
+ onPageFinished: (String url) {
+ pageLoaded.complete(null);
+ },
+ initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
+ allowsInlineMediaPlayback: true,
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+ await pageLoaded.future;
+
+ // Pump once to trigger the video play.
+ await tester.pump();
+
+ // Makes sure we get the correct event that indicates the video is actually playing.
+ await videoPlaying.future;
+
+ final String fullScreen =
+ await controller.runJavascriptReturningResult('isFullScreen();');
+ expect(fullScreen, _webviewBool(false));
+ });
+
+ // allowsInlineMediaPlayback is a noop on Android, so it is skipped.
+ testWidgets(
+ 'Video plays full screen when allowsInlineMediaPlayback is false',
+ (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ final Completer<void> pageLoaded = Completer<void>();
+ final Completer<void> videoPlaying = Completer<void>();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ javascriptChannels: <JavascriptChannel>{
+ JavascriptChannel(
+ name: 'VideoTestTime',
+ onMessageReceived: (JavascriptMessage message) {
+ final double currentTime = double.parse(message.message);
+ // Let it play for at least 1 second to make sure the related video's properties are set.
+ if (currentTime > 1 && !videoPlaying.isCompleted) {
+ videoPlaying.complete(null);
+ }
+ },
+ ),
+ },
+ onPageFinished: (String url) {
+ pageLoaded.complete(null);
+ },
+ initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+ await pageLoaded.future;
+
+ // Pump once to trigger the video play.
+ await tester.pump();
+
+ // Makes sure we get the correct event that indicates the video is actually playing.
+ await videoPlaying.future;
+
+ final String fullScreen =
+ await controller.runJavascriptReturningResult('isFullScreen();');
+ expect(fullScreen, _webviewBool(true));
+ }, skip: Platform.isAndroid);
+ });
+
+ group('Audio playback policy', () {
+ late String audioTestBase64;
+ setUpAll(() async {
+ final ByteData audioData =
+ await rootBundle.load('assets/sample_audio.ogg');
+ final String base64AudioData =
+ base64Encode(Uint8List.view(audioData.buffer));
+ final String audioTest = '''
+ <!DOCTYPE html><html>
+ <head><title>Audio auto play</title>
+ <script type="text/javascript">
+ function play() {
+ var audio = document.getElementById("audio");
+ audio.play();
+ }
+ function isPaused() {
+ var audio = document.getElementById("audio");
+ return audio.paused;
+ }
+ </script>
+ </head>
+ <body onload="play();">
+ <audio controls id="audio">
+ <source src="data:audio/ogg;charset=utf-8;base64,$base64AudioData">
+ </audio>
+ </body>
+ </html>
+ ''';
+ audioTestBase64 = base64Encode(const Utf8Encoder().convert(audioTest));
+ });
+
+ testWidgets('Auto media playback', (WidgetTester tester) async {
+ Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ Completer<void> pageStarted = Completer<void>();
+ Completer<void> pageLoaded = Completer<void>();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ onPageStarted: (String url) {
+ pageStarted.complete(null);
+ },
+ onPageFinished: (String url) {
+ pageLoaded.complete(null);
+ },
+ initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
+ ),
+ ),
+ );
+ WebViewController controller = await controllerCompleter.future;
+ await pageStarted.future;
+ await pageLoaded.future;
+
+ String isPaused =
+ await controller.runJavascriptReturningResult('isPaused();');
+ expect(isPaused, _webviewBool(false));
+
+ controllerCompleter = Completer<WebViewController>();
+ pageStarted = Completer<void>();
+ pageLoaded = Completer<void>();
+
+ // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ onPageStarted: (String url) {
+ pageStarted.complete(null);
+ },
+ onPageFinished: (String url) {
+ pageLoaded.complete(null);
+ },
+ ),
+ ),
+ );
+
+ controller = await controllerCompleter.future;
+ await pageStarted.future;
+ await pageLoaded.future;
+
+ isPaused = await controller.runJavascriptReturningResult('isPaused();');
+ expect(isPaused, _webviewBool(true));
+ });
+
+ testWidgets('Changes to initialMediaPlaybackPolicy are ignored',
+ (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ Completer<void> pageStarted = Completer<void>();
+ Completer<void> pageLoaded = Completer<void>();
+
+ final GlobalKey key = GlobalKey();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: key,
+ initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ onPageStarted: (String url) {
+ pageStarted.complete(null);
+ },
+ onPageFinished: (String url) {
+ pageLoaded.complete(null);
+ },
+ initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+ await pageStarted.future;
+ await pageLoaded.future;
+
+ String isPaused =
+ await controller.runJavascriptReturningResult('isPaused();');
+ expect(isPaused, _webviewBool(false));
+
+ pageStarted = Completer<void>();
+ pageLoaded = Completer<void>();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: key,
+ initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ onPageStarted: (String url) {
+ pageStarted.complete(null);
+ },
+ onPageFinished: (String url) {
+ pageLoaded.complete(null);
+ },
+ ),
+ ),
+ );
+
+ await controller.reload();
+
+ await pageStarted.future;
+ await pageLoaded.future;
+
+ isPaused = await controller.runJavascriptReturningResult('isPaused();');
+ expect(isPaused, _webviewBool(false));
+ });
+ });
+
+ testWidgets('getTitle', (WidgetTester tester) async {
+ const String getTitleTest = '''
+ <!DOCTYPE html><html>
+ <head><title>Some title</title>
+ </head>
+ <body>
+ </body>
+ </html>
+ ''';
+ final String getTitleTestBase64 =
+ base64Encode(const Utf8Encoder().convert(getTitleTest));
+ final Completer<void> pageStarted = Completer<void>();
+ final Completer<void> pageLoaded = Completer<void>();
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ initialUrl: 'data:text/html;charset=utf-8;base64,$getTitleTestBase64',
+ javascriptMode: JavascriptMode.unrestricted,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ onPageStarted: (String url) {
+ pageStarted.complete(null);
+ },
+ onPageFinished: (String url) {
+ pageLoaded.complete(null);
+ },
+ ),
+ ),
+ );
+
+ final WebViewController controller = await controllerCompleter.future;
+ await pageStarted.future;
+ await pageLoaded.future;
+
+ // On at least iOS, it does not appear to be guaranteed that the native
+ // code has the title when the page load completes. Execute some JavaScript
+ // before checking the title to ensure that the page has been fully parsed
+ // and processed.
+ await controller.runJavascript('1;');
+
+ final String? title = await controller.getTitle();
+ expect(title, 'Some title');
+ });
+
+ group('Programmatic Scroll', () {
+ testWidgets('setAndGetScrollPosition', (WidgetTester tester) async {
+ const String scrollTestPage = '''
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <style>
+ body {
+ height: 100%;
+ width: 100%;
+ }
+ #container{
+ width:5000px;
+ height:5000px;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="container"/>
+ </body>
+ </html>
+ ''';
+
+ final String scrollTestPageBase64 =
+ base64Encode(const Utf8Encoder().convert(scrollTestPage));
+
+ final Completer<void> pageLoaded = Completer<void>();
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ initialUrl:
+ 'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ onPageFinished: (String url) {
+ pageLoaded.complete(null);
+ },
+ ),
+ ),
+ );
+
+ final WebViewController controller = await controllerCompleter.future;
+ await pageLoaded.future;
+
+ await tester.pumpAndSettle(const Duration(seconds: 3));
+
+ int scrollPosX = await controller.getScrollX();
+ int scrollPosY = await controller.getScrollY();
+
+ // Check scrollTo()
+ const int X_SCROLL = 123;
+ const int Y_SCROLL = 321;
+ // Get the initial position; this ensures that scrollTo is actually
+ // changing something, but also gives the native view's scroll position
+ // time to settle.
+ expect(scrollPosX, isNot(X_SCROLL));
+ expect(scrollPosX, isNot(Y_SCROLL));
+
+ await controller.scrollTo(X_SCROLL, Y_SCROLL);
+ scrollPosX = await controller.getScrollX();
+ scrollPosY = await controller.getScrollY();
+ expect(scrollPosX, X_SCROLL);
+ expect(scrollPosY, Y_SCROLL);
+
+ // Check scrollBy() (on top of scrollTo())
+ await controller.scrollBy(X_SCROLL, Y_SCROLL);
+ scrollPosX = await controller.getScrollX();
+ scrollPosY = await controller.getScrollY();
+ expect(scrollPosX, X_SCROLL * 2);
+ expect(scrollPosY, Y_SCROLL * 2);
+ });
+ });
+
+ // Minimal end-to-end testing of the legacy Android implementation.
+ group('AndroidWebView (virtual display)', () {
+ setUpAll(() {
+ WebView.platform = AndroidWebView();
+ });
+
+ tearDownAll(() {
+ WebView.platform = null;
+ });
+
+ testWidgets('initialUrl', (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ final Completer<void> pageFinishedCompleter = Completer<void>();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: primaryUrl,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ onPageFinished: pageFinishedCompleter.complete,
+ ),
+ ),
+ );
+
+ final WebViewController controller = await controllerCompleter.future;
+ await pageFinishedCompleter.future;
+
+ final String? currentUrl = await controller.currentUrl();
+ expect(currentUrl, primaryUrl);
+ });
+ }, skip: !Platform.isAndroid);
+
+ group('NavigationDelegate', () {
+ const String blankPage = '<!DOCTYPE html><head></head><body></body></html>';
+ final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,'
+ '${base64Encode(const Utf8Encoder().convert(blankPage))}';
+
+ testWidgets('can allow requests', (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ final StreamController<String> pageLoads =
+ StreamController<String>.broadcast();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: blankPageEncoded,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ navigationDelegate: (NavigationRequest request) {
+ return (request.url.contains('youtube.com'))
+ ? NavigationDecision.prevent
+ : NavigationDecision.navigate;
+ },
+ onPageFinished: (String url) => pageLoads.add(url),
+ ),
+ ),
+ );
+
+ await pageLoads.stream.first; // Wait for initial page load.
+ final WebViewController controller = await controllerCompleter.future;
+ await controller.runJavascript('location.href = "$secondaryUrl"');
+
+ await pageLoads.stream.first; // Wait for the next page load.
+ final String? currentUrl = await controller.currentUrl();
+ expect(currentUrl, secondaryUrl);
+ });
+
+ testWidgets('onWebResourceError', (WidgetTester tester) async {
+ final Completer<WebResourceError> errorCompleter =
+ Completer<WebResourceError>();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: 'https://www.notawebsite..com',
+ onWebResourceError: (WebResourceError error) {
+ errorCompleter.complete(error);
+ },
+ ),
+ ),
+ );
+
+ final WebResourceError error = await errorCompleter.future;
+ expect(error, isNotNull);
+
+ if (Platform.isIOS) {
+ expect(error.domain, isNotNull);
+ expect(error.failingUrl, isNull);
+ } else if (Platform.isAndroid) {
+ expect(error.errorType, isNotNull);
+ expect(error.failingUrl?.startsWith('https://www.notawebsite..com'),
+ isTrue);
+ }
+ });
+
+ testWidgets('onWebResourceError is not called with valid url',
+ (WidgetTester tester) async {
+ final Completer<WebResourceError> errorCompleter =
+ Completer<WebResourceError>();
+ final Completer<void> pageFinishCompleter = Completer<void>();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl:
+ 'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+',
+ onWebResourceError: (WebResourceError error) {
+ errorCompleter.complete(error);
+ },
+ onPageFinished: (_) => pageFinishCompleter.complete(),
+ ),
+ ),
+ );
+
+ expect(errorCompleter.future, doesNotComplete);
+ await pageFinishCompleter.future;
+ });
+
+ testWidgets(
+ 'onWebResourceError only called for main frame',
+ (WidgetTester tester) async {
+ const String iframeTest = '''
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <title>WebResourceError test</title>
+ </head>
+ <body>
+ <iframe src="https://notawebsite..com"></iframe>
+ </body>
+ </html>
+ ''';
+ final String iframeTestBase64 =
+ base64Encode(const Utf8Encoder().convert(iframeTest));
+
+ final Completer<WebResourceError> errorCompleter =
+ Completer<WebResourceError>();
+ final Completer<void> pageFinishCompleter = Completer<void>();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl:
+ 'data:text/html;charset=utf-8;base64,$iframeTestBase64',
+ onWebResourceError: (WebResourceError error) {
+ errorCompleter.complete(error);
+ },
+ onPageFinished: (_) => pageFinishCompleter.complete(),
+ ),
+ ),
+ );
+
+ expect(errorCompleter.future, doesNotComplete);
+ await pageFinishCompleter.future;
+ },
+ );
+
+ testWidgets('can block requests', (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ final StreamController<String> pageLoads =
+ StreamController<String>.broadcast();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: blankPageEncoded,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ navigationDelegate: (NavigationRequest request) {
+ return (request.url.contains('youtube.com'))
+ ? NavigationDecision.prevent
+ : NavigationDecision.navigate;
+ },
+ onPageFinished: (String url) => pageLoads.add(url),
+ ),
+ ),
+ );
+
+ await pageLoads.stream.first; // Wait for initial page load.
+ final WebViewController controller = await controllerCompleter.future;
+ await controller
+ .runJavascript('location.href = "https://www.youtube.com/"');
+
+ // There should never be any second page load, since our new URL is
+ // blocked. Still wait for a potential page change for some time in order
+ // to give the test a chance to fail.
+ await pageLoads.stream.first
+ .timeout(const Duration(milliseconds: 500), onTimeout: () => '');
+ final String? currentUrl = await controller.currentUrl();
+ expect(currentUrl, isNot(contains('youtube.com')));
+ });
+
+ testWidgets('supports asynchronous decisions', (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ final StreamController<String> pageLoads =
+ StreamController<String>.broadcast();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: blankPageEncoded,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ navigationDelegate: (NavigationRequest request) async {
+ NavigationDecision decision = NavigationDecision.prevent;
+ decision = await Future<NavigationDecision>.delayed(
+ const Duration(milliseconds: 10),
+ () => NavigationDecision.navigate);
+ return decision;
+ },
+ onPageFinished: (String url) => pageLoads.add(url),
+ ),
+ ),
+ );
+
+ await pageLoads.stream.first; // Wait for initial page load.
+ final WebViewController controller = await controllerCompleter.future;
+ await controller.runJavascript('location.href = "$secondaryUrl"');
+
+ await pageLoads.stream.first; // Wait for second page to load.
+ final String? currentUrl = await controller.currentUrl();
+ expect(currentUrl, secondaryUrl);
+ });
+ });
+
+ testWidgets('launches with gestureNavigationEnabled on iOS',
+ (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: SizedBox(
+ width: 400,
+ height: 300,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: primaryUrl,
+ gestureNavigationEnabled: true,
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+ final String? currentUrl = await controller.currentUrl();
+ expect(currentUrl, primaryUrl);
+ });
+
+ testWidgets('target _blank opens in same window',
+ (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ final Completer<void> pageLoaded = Completer<void>();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ onPageFinished: (String url) {
+ pageLoaded.complete(null);
+ },
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+ await controller.runJavascript('window.open("$primaryUrl", "_blank")');
+ await pageLoaded.future;
+ final String? currentUrl = await controller.currentUrl();
+ expect(currentUrl, primaryUrl);
+ });
+
+ testWidgets(
+ 'can open new window and go back',
+ (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+ Completer<void> pageLoaded = Completer<void>();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ javascriptMode: JavascriptMode.unrestricted,
+ onPageFinished: (String url) {
+ pageLoaded.complete();
+ },
+ initialUrl: primaryUrl,
+ ),
+ ),
+ );
+ final WebViewController controller = await controllerCompleter.future;
+ expect(controller.currentUrl(), completion(primaryUrl));
+ await pageLoaded.future;
+ pageLoaded = Completer<void>();
+
+ await controller.runJavascript('window.open("$secondaryUrl")');
+ await pageLoaded.future;
+ pageLoaded = Completer<void>();
+ expect(controller.currentUrl(), completion(secondaryUrl));
+
+ expect(controller.canGoBack(), completion(true));
+ await controller.goBack();
+ await pageLoaded.future;
+ await expectLater(controller.currentUrl(), completion(primaryUrl));
+ },
+ );
+
+ testWidgets(
+ 'clearCache should clear local storage',
+ (WidgetTester tester) async {
+ final Completer<WebViewController> controllerCompleter =
+ Completer<WebViewController>();
+
+ Completer<void> pageLoadCompleter = Completer<void>();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: WebView(
+ key: GlobalKey(),
+ initialUrl: primaryUrl,
+ javascriptMode: JavascriptMode.unrestricted,
+ onPageFinished: (_) => pageLoadCompleter.complete(),
+ onWebViewCreated: (WebViewController controller) {
+ controllerCompleter.complete(controller);
+ },
+ ),
+ ),
+ );
+
+ await pageLoadCompleter.future;
+ pageLoadCompleter = Completer<void>();
+
+ final WebViewController controller = await controllerCompleter.future;
+ await controller.runJavascript('localStorage.setItem("myCat", "Tom");');
+ final String myCatItem = await controller.runJavascriptReturningResult(
+ 'localStorage.getItem("myCat");',
+ );
+ expect(myCatItem, _webviewString('Tom'));
+
+ await controller.clearCache();
+ await pageLoadCompleter.future;
+
+ late final String? nullItem;
+ try {
+ nullItem = await controller.runJavascriptReturningResult(
+ 'localStorage.getItem("myCat");',
+ );
+ } catch (exception) {
+ if (defaultTargetPlatform == TargetPlatform.iOS &&
+ exception is ArgumentError &&
+ (exception.message as String).contains(
+ 'Result of JavaScript execution returned a `null` value.')) {
+ nullItem = '<null>';
+ }
+ }
+ expect(nullItem, _webviewNull());
+ },
+ );
+}
+
+// JavaScript booleans evaluate to different string values on Android and iOS.
+// This utility method returns the string boolean value of the current platform.
+String _webviewBool(bool value) {
+ if (defaultTargetPlatform == TargetPlatform.iOS) {
+ return value ? '1' : '0';
+ }
+ return value ? 'true' : 'false';
+}
+
+// JavaScript `null` evaluate to different string values on Android and iOS.
+// This utility method returns the string boolean value of the current platform.
+String _webviewNull() {
+ if (defaultTargetPlatform == TargetPlatform.iOS) {
+ return '<null>';
+ }
+ return 'null';
+}
+
+// JavaScript String evaluate to different string values on Android and iOS.
+// This utility method returns the string boolean value of the current platform.
+String _webviewString(String value) {
+ if (defaultTargetPlatform == TargetPlatform.iOS) {
+ return value;
+ }
+ return '"$value"';
+}
+
+/// Returns the value used for the HTTP User-Agent: request header in subsequent HTTP requests.
+Future<String> _getUserAgent(WebViewController controller) async {
+ return _runJavascriptReturningResult(controller, 'navigator.userAgent;');
+}
+
+Future<String> _runJavascriptReturningResult(
+ WebViewController controller, String js) async {
+ if (defaultTargetPlatform == TargetPlatform.iOS) {
+ return controller.runJavascriptReturningResult(js);
+ }
+ return jsonDecode(await controller.runJavascriptReturningResult(js))
+ as String;
+}
+
+class ResizableWebView extends StatefulWidget {
+ const ResizableWebView({
+ super.key,
+ required this.onResize,
+ required this.onPageFinished,
+ });
+
+ final JavascriptMessageHandler onResize;
+ final VoidCallback onPageFinished;
+
+ @override
+ State<StatefulWidget> createState() => ResizableWebViewState();
+}
+
+class ResizableWebViewState extends State<ResizableWebView> {
+ double webViewWidth = 200;
+ double webViewHeight = 200;
+
+ static const String resizePage = '''
+ <!DOCTYPE html><html>
+ <head><title>Resize test</title>
+ <script type="text/javascript">
+ function onResize() {
+ Resize.postMessage("resize");
+ }
+ function onLoad() {
+ window.onresize = onResize;
+ }
+ </script>
+ </head>
+ <body onload="onLoad();" bgColor="blue">
+ </body>
+ </html>
+ ''';
+
+ @override
+ Widget build(BuildContext context) {
+ final String resizeTestBase64 =
+ base64Encode(const Utf8Encoder().convert(resizePage));
+ return Directionality(
+ textDirection: TextDirection.ltr,
+ child: Column(
+ children: <Widget>[
+ SizedBox(
+ width: webViewWidth,
+ height: webViewHeight,
+ child: WebView(
+ initialUrl:
+ 'data:text/html;charset=utf-8;base64,$resizeTestBase64',
+ javascriptChannels: <JavascriptChannel>{
+ JavascriptChannel(
+ name: 'Resize',
+ onMessageReceived: widget.onResize,
+ ),
+ },
+ onPageFinished: (_) => widget.onPageFinished(),
+ javascriptMode: JavascriptMode.unrestricted,
+ ),
+ ),
+ TextButton(
+ key: const Key('resizeButton'),
+ onPressed: () {
+ setState(() {
+ webViewWidth += 100.0;
+ webViewHeight += 100.0;
+ });
+ },
+ child: const Text('ResizeButton'),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart
index 8dd8321..7763327 100644
--- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart
+++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart
@@ -19,6 +19,8 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:webview_flutter/webview_flutter.dart';
+import 'package:webview_flutter_android/webview_flutter_android.dart';
+import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
Future<void> main() async {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
@@ -43,163 +45,93 @@
final String secondaryUrl = '$prefixUrl/secondary.txt';
final String headersUrl = '$prefixUrl/headers';
- testWidgets('initialUrl', (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- final Completer<void> pageFinishedCompleter = Completer<void>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: primaryUrl,
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- onPageFinished: pageFinishedCompleter.complete,
- ),
- ),
- );
+ testWidgets('loadRequest', (WidgetTester tester) async {
+ final Completer<void> pageFinished = Completer<void>();
- final WebViewController controller = await controllerCompleter.future;
- await pageFinishedCompleter.future;
+ final WebViewController controller = WebViewController()
+ ..setNavigationDelegate(
+ NavigationDelegate(onPageFinished: (_) => pageFinished.complete()),
+ )
+ ..loadRequest(Uri.parse(primaryUrl));
+
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+
+ await pageFinished.future;
final String? currentUrl = await controller.currentUrl();
expect(currentUrl, primaryUrl);
});
- testWidgets('loadUrl', (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- final StreamController<String> pageLoads = StreamController<String>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: primaryUrl,
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- onPageFinished: (String url) {
- pageLoads.add(url);
- },
- ),
- ),
- );
- final WebViewController controller = await controllerCompleter.future;
+ testWidgets('runJavaScriptReturningResult', (WidgetTester tester) async {
+ final Completer<void> pageFinished = Completer<void>();
- await controller.loadUrl(secondaryUrl);
+ final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(
+ NavigationDelegate(onPageFinished: (_) => pageFinished.complete()),
+ )
+ ..loadRequest(Uri.parse(primaryUrl));
+
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+
+ await pageFinished.future;
+
await expectLater(
- pageLoads.stream.firstWhere((String url) => url == secondaryUrl),
- completion(secondaryUrl),
+ controller.runJavaScriptReturningResult('1 + 1'),
+ completion(2),
);
});
- testWidgets('evaluateJavascript', (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: primaryUrl,
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- ),
- ),
- );
- final WebViewController controller = await controllerCompleter.future;
- // ignore: deprecated_member_use
- final String result = await controller.evaluateJavascript('1 + 1');
- expect(result, equals('2'));
- });
-
- testWidgets('loadUrl with headers', (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- final StreamController<String> pageStarts = StreamController<String>();
- final StreamController<String> pageLoads = StreamController<String>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: primaryUrl,
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- onPageStarted: (String url) {
- pageStarts.add(url);
- },
- onPageFinished: (String url) {
- pageLoads.add(url);
- },
- ),
- ),
- );
- final WebViewController controller = await controllerCompleter.future;
+ testWidgets('loadRequest with headers', (WidgetTester tester) async {
final Map<String, String> headers = <String, String>{
'test_header': 'flutter_test_header'
};
- await controller.loadUrl(headersUrl, headers: headers);
- final String? currentUrl = await controller.currentUrl();
- expect(currentUrl, headersUrl);
- await pageStarts.stream.firstWhere((String url) => url == currentUrl);
- await pageLoads.stream.firstWhere((String url) => url == currentUrl);
+ final StreamController<String> pageLoads = StreamController<String>();
- final String content = await controller
- .runJavascriptReturningResult('document.documentElement.innerText');
+ final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(
+ NavigationDelegate(onPageFinished: (String url) => pageLoads.add(url)),
+ );
+
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+
+ controller.loadRequest(Uri.parse(headersUrl), headers: headers);
+
+ await pageLoads.stream.firstWhere((String url) => url == headersUrl);
+
+ final String content = await controller.runJavaScriptReturningResult(
+ 'document.documentElement.innerText',
+ ) as String;
expect(content.contains('flutter_test_header'), isTrue);
});
testWidgets('JavascriptChannel', (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- final Completer<void> pageStarted = Completer<void>();
- final Completer<void> pageLoaded = Completer<void>();
+ final Completer<void> pageFinished = Completer<void>();
+ final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(
+ NavigationDelegate(onPageFinished: (_) => pageFinished.complete()),
+ );
+
final Completer<String> channelCompleter = Completer<String>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- // This is the data URL for: '<!DOCTYPE html>'
- initialUrl:
- 'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+',
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- javascriptChannels: <JavascriptChannel>{
- JavascriptChannel(
- name: 'Echo',
- onMessageReceived: (JavascriptMessage message) {
- channelCompleter.complete(message.message);
- },
- ),
- },
- onPageStarted: (String url) {
- pageStarted.complete(null);
- },
- onPageFinished: (String url) {
- pageLoaded.complete(null);
- },
- ),
- ),
+ await controller.addJavaScriptChannel(
+ 'Echo',
+ onMessageReceived: (JavaScriptMessage message) {
+ channelCompleter.complete(message.message);
+ },
);
- final WebViewController controller = await controllerCompleter.future;
- await pageStarted.future;
- await pageLoaded.future;
- expect(channelCompleter.isCompleted, isFalse);
- await controller.runJavascript('Echo.postMessage("hello");');
+ await controller.loadHtmlString(
+ 'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+',
+ );
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+
+ await pageFinished.future;
+
+ await controller.runJavaScript('Echo.postMessage("hello");');
await expectLater(channelCompleter.future, completion('hello'));
});
@@ -210,7 +142,7 @@
bool resizeButtonTapped = false;
await tester.pumpWidget(ResizableWebView(
- onResize: (_) {
+ onResize: () {
if (resizeButtonTapped) {
buttonTapResizeCompleter.complete();
} else {
@@ -219,6 +151,7 @@
},
onPageFinished: () => onPageFinished.complete(),
));
+
await onPageFinished.future;
// Wait for a potential call to resize after page is loaded.
await initialResizeCompleter.future.timeout(
@@ -227,98 +160,30 @@
);
resizeButtonTapped = true;
+
await tester.tap(find.byKey(const ValueKey<String>('resizeButton')));
await tester.pumpAndSettle();
- expect(buttonTapResizeCompleter.future, completes);
+
+ await expectLater(buttonTapResizeCompleter.future, completes);
});
testWidgets('set custom userAgent', (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter1 =
- Completer<WebViewController>();
- final GlobalKey globalKey = GlobalKey();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: globalKey,
- initialUrl: 'about:blank',
- javascriptMode: JavascriptMode.unrestricted,
- userAgent: 'Custom_User_Agent1',
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter1.complete(controller);
- },
- ),
- ),
- );
- final WebViewController controller1 = await controllerCompleter1.future;
- final String customUserAgent1 = await _getUserAgent(controller1);
- expect(customUserAgent1, 'Custom_User_Agent1');
- // rebuild the WebView with a different user agent.
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: globalKey,
- initialUrl: 'about:blank',
- javascriptMode: JavascriptMode.unrestricted,
- userAgent: 'Custom_User_Agent2',
- ),
- ),
- );
+ final Completer<void> pageFinished = Completer<void>();
- final String customUserAgent2 = await _getUserAgent(controller1);
- expect(customUserAgent2, 'Custom_User_Agent2');
- });
+ final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(NavigationDelegate(
+ onPageFinished: (_) => pageFinished.complete(),
+ ))
+ ..setUserAgent('Custom_User_Agent1')
+ ..loadRequest(Uri.parse('about:blank'));
- testWidgets('use default platform userAgent after webView is rebuilt',
- (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- final GlobalKey globalKey = GlobalKey();
- // Build the webView with no user agent to get the default platform user agent.
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: globalKey,
- initialUrl: primaryUrl,
- javascriptMode: JavascriptMode.unrestricted,
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- ),
- ),
- );
- final WebViewController controller = await controllerCompleter.future;
- final String defaultPlatformUserAgent = await _getUserAgent(controller);
- // rebuild the WebView with a custom user agent.
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: globalKey,
- initialUrl: 'about:blank',
- javascriptMode: JavascriptMode.unrestricted,
- userAgent: 'Custom_User_Agent',
- ),
- ),
- );
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+
+ await pageFinished.future;
+
final String customUserAgent = await _getUserAgent(controller);
- expect(customUserAgent, 'Custom_User_Agent');
- // rebuilds the WebView with no user agent.
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: globalKey,
- initialUrl: 'about:blank',
- javascriptMode: JavascriptMode.unrestricted,
- ),
- ),
- );
-
- final String customUserAgent2 = await _getUserAgent(controller);
- expect(customUserAgent2, defaultPlatformUserAgent);
+ expect(customUserAgent, 'Custom_User_Agent1');
});
group('Video playback policy', () {
@@ -362,219 +227,156 @@
});
testWidgets('Auto media playback', (WidgetTester tester) async {
- Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
Completer<void> pageLoaded = Completer<void>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- onPageFinished: (String url) {
- pageLoaded.complete(null);
- },
- initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
- ),
- ),
- );
- WebViewController controller = await controllerCompleter.future;
- await pageLoaded.future;
+ late PlatformWebViewControllerCreationParams params;
+ if (defaultTargetPlatform == TargetPlatform.iOS) {
+ params = WebKitWebViewControllerCreationParams(
+ mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
+ );
+ } else {
+ params = const PlatformWebViewControllerCreationParams();
+ }
- String isPaused =
- await controller.runJavascriptReturningResult('isPaused();');
- expect(isPaused, _webviewBool(false));
+ WebViewController controller =
+ WebViewController.fromPlatformCreationParams(params)
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(
+ NavigationDelegate(onPageFinished: (_) => pageLoaded.complete()),
+ );
- controllerCompleter = Completer<WebViewController>();
- pageLoaded = Completer<void>();
+ if (controller.platform is AndroidWebViewController) {
+ (controller.platform as AndroidWebViewController)
+ .setMediaPlaybackRequiresUserGesture(false);
+ }
- // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- onPageFinished: (String url) {
- pageLoaded.complete(null);
- },
- ),
- ),
+ await controller.loadRequest(
+ Uri.parse('data:text/html;charset=utf-8;base64,$videoTestBase64'),
);
- controller = await controllerCompleter.future;
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+
await pageLoaded.future;
- isPaused = await controller.runJavascriptReturningResult('isPaused();');
- expect(isPaused, _webviewBool(true));
- });
-
- testWidgets('Changes to initialMediaPlaybackPolicy are ignored',
- (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- Completer<void> pageLoaded = Completer<void>();
-
- final GlobalKey key = GlobalKey();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: key,
- initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- onPageFinished: (String url) {
- pageLoaded.complete(null);
- },
- initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
- ),
- ),
- );
- final WebViewController controller = await controllerCompleter.future;
- await pageLoaded.future;
-
- String isPaused =
- await controller.runJavascriptReturningResult('isPaused();');
- expect(isPaused, _webviewBool(false));
+ bool isPaused =
+ await controller.runJavaScriptReturningResult('isPaused();') as bool;
+ expect(isPaused, false);
pageLoaded = Completer<void>();
+ controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(
+ NavigationDelegate(onPageFinished: (_) => pageLoaded.complete()),
+ )
+ ..loadRequest(
+ Uri.parse('data:text/html;charset=utf-8;base64,$videoTestBase64'),
+ );
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: key,
- initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- onPageFinished: (String url) {
- pageLoaded.complete(null);
- },
- ),
- ),
- );
-
- await controller.reload();
+ await tester.pumpWidget(WebViewWidget(controller: controller));
await pageLoaded.future;
- isPaused = await controller.runJavascriptReturningResult('isPaused();');
- expect(isPaused, _webviewBool(false));
+ isPaused =
+ await controller.runJavaScriptReturningResult('isPaused();') as bool;
+ expect(isPaused, true);
});
- testWidgets('Video plays inline when allowsInlineMediaPlayback is true',
- (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
+ testWidgets('Video plays inline', (WidgetTester tester) async {
final Completer<void> pageLoaded = Completer<void>();
final Completer<void> videoPlaying = Completer<void>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- javascriptChannels: <JavascriptChannel>{
- JavascriptChannel(
- name: 'VideoTestTime',
- onMessageReceived: (JavascriptMessage message) {
- final double currentTime = double.parse(message.message);
- // Let it play for at least 1 second to make sure the related video's properties are set.
- if (currentTime > 1 && !videoPlaying.isCompleted) {
- videoPlaying.complete(null);
- }
- },
- ),
- },
- onPageFinished: (String url) {
- pageLoaded.complete(null);
- },
- initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
- allowsInlineMediaPlayback: true,
- ),
- ),
- );
- final WebViewController controller = await controllerCompleter.future;
- await pageLoaded.future;
+ late PlatformWebViewControllerCreationParams params;
+ if (defaultTargetPlatform == TargetPlatform.iOS) {
+ params = WebKitWebViewControllerCreationParams(
+ mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
+ allowsInlineMediaPlayback: true,
+ );
+ } else {
+ params = const PlatformWebViewControllerCreationParams();
+ }
+ final WebViewController controller =
+ WebViewController.fromPlatformCreationParams(params)
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(
+ NavigationDelegate(onPageFinished: (_) => pageLoaded.complete()),
+ )
+ ..addJavaScriptChannel(
+ 'VideoTestTime',
+ onMessageReceived: (JavaScriptMessage message) {
+ final double currentTime = double.parse(message.message);
+ // Let it play for at least 1 second to make sure the related video's properties are set.
+ if (currentTime > 1 && !videoPlaying.isCompleted) {
+ videoPlaying.complete(null);
+ }
+ },
+ );
- // Pump once to trigger the video play.
- await tester.pump();
+ if (controller.platform is AndroidWebViewController) {
+ (controller.platform as AndroidWebViewController)
+ .setMediaPlaybackRequiresUserGesture(false);
+ }
+
+ await controller.loadRequest(
+ Uri.parse('data:text/html;charset=utf-8;base64,$videoTestBase64'),
+ );
+
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+ await tester.pumpAndSettle();
+
+ await pageLoaded.future;
// Makes sure we get the correct event that indicates the video is actually playing.
await videoPlaying.future;
- final String fullScreen =
- await controller.runJavascriptReturningResult('isFullScreen();');
- expect(fullScreen, _webviewBool(false));
+ final bool fullScreen = await controller
+ .runJavaScriptReturningResult('isFullScreen();') as bool;
+ expect(fullScreen, false);
});
// allowsInlineMediaPlayback is a noop on Android, so it is skipped.
testWidgets(
'Video plays full screen when allowsInlineMediaPlayback is false',
(WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
final Completer<void> pageLoaded = Completer<void>();
final Completer<void> videoPlaying = Completer<void>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- javascriptChannels: <JavascriptChannel>{
- JavascriptChannel(
- name: 'VideoTestTime',
- onMessageReceived: (JavascriptMessage message) {
- final double currentTime = double.parse(message.message);
- // Let it play for at least 1 second to make sure the related video's properties are set.
- if (currentTime > 1 && !videoPlaying.isCompleted) {
- videoPlaying.complete(null);
- }
- },
- ),
- },
- onPageFinished: (String url) {
- pageLoaded.complete(null);
- },
- initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
- ),
+ final WebViewController controller =
+ WebViewController.fromPlatformCreationParams(
+ WebKitWebViewControllerCreationParams(
+ mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
),
- );
- final WebViewController controller = await controllerCompleter.future;
- await pageLoaded.future;
+ )
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(
+ NavigationDelegate(onPageFinished: (_) => pageLoaded.complete()),
+ )
+ ..addJavaScriptChannel(
+ 'VideoTestTime',
+ onMessageReceived: (JavaScriptMessage message) {
+ final double currentTime = double.parse(message.message);
+ // Let it play for at least 1 second to make sure the related video's properties are set.
+ if (currentTime > 1 && !videoPlaying.isCompleted) {
+ videoPlaying.complete(null);
+ }
+ },
+ )
+ ..loadRequest(
+ Uri.parse(
+ 'data:text/html;charset=utf-8;base64,$videoTestBase64',
+ ),
+ );
- // Pump once to trigger the video play.
- await tester.pump();
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+ await tester.pumpAndSettle();
+
+ await pageLoaded.future;
// Makes sure we get the correct event that indicates the video is actually playing.
await videoPlaying.future;
- final String fullScreen =
- await controller.runJavascriptReturningResult('isFullScreen();');
- expect(fullScreen, _webviewBool(true));
+ final bool fullScreen = await controller
+ .runJavaScriptReturningResult('isFullScreen();') as bool;
+ expect(fullScreen, true);
}, skip: Platform.isAndroid);
});
@@ -610,138 +412,60 @@
});
testWidgets('Auto media playback', (WidgetTester tester) async {
- Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- Completer<void> pageStarted = Completer<void>();
Completer<void> pageLoaded = Completer<void>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- onPageStarted: (String url) {
- pageStarted.complete(null);
- },
- onPageFinished: (String url) {
- pageLoaded.complete(null);
- },
- initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
- ),
- ),
+ late PlatformWebViewControllerCreationParams params;
+ if (defaultTargetPlatform == TargetPlatform.iOS) {
+ params = WebKitWebViewControllerCreationParams(
+ mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
+ );
+ } else {
+ params = const PlatformWebViewControllerCreationParams();
+ }
+
+ WebViewController controller =
+ WebViewController.fromPlatformCreationParams(params)
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(
+ NavigationDelegate(onPageFinished: (_) => pageLoaded.complete()),
+ );
+
+ if (controller.platform is AndroidWebViewController) {
+ (controller.platform as AndroidWebViewController)
+ .setMediaPlaybackRequiresUserGesture(false);
+ }
+
+ await controller.loadRequest(
+ Uri.parse('data:text/html;charset=utf-8;base64,$audioTestBase64'),
);
- WebViewController controller = await controllerCompleter.future;
- await pageStarted.future;
+
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+ await tester.pumpAndSettle();
+
await pageLoaded.future;
- String isPaused =
- await controller.runJavascriptReturningResult('isPaused();');
- expect(isPaused, _webviewBool(false));
+ bool isPaused =
+ await controller.runJavaScriptReturningResult('isPaused();') as bool;
+ expect(isPaused, false);
- controllerCompleter = Completer<WebViewController>();
- pageStarted = Completer<void>();
pageLoaded = Completer<void>();
+ controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(
+ NavigationDelegate(onPageFinished: (_) => pageLoaded.complete()),
+ )
+ ..loadRequest(
+ Uri.parse('data:text/html;charset=utf-8;base64,$audioTestBase64'),
+ );
- // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- onPageStarted: (String url) {
- pageStarted.complete(null);
- },
- onPageFinished: (String url) {
- pageLoaded.complete(null);
- },
- ),
- ),
- );
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+ await tester.pumpAndSettle();
- controller = await controllerCompleter.future;
- await pageStarted.future;
await pageLoaded.future;
- isPaused = await controller.runJavascriptReturningResult('isPaused();');
- expect(isPaused, _webviewBool(true));
- });
-
- testWidgets('Changes to initialMediaPlaybackPolicy are ignored',
- (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- Completer<void> pageStarted = Completer<void>();
- Completer<void> pageLoaded = Completer<void>();
-
- final GlobalKey key = GlobalKey();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: key,
- initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- onPageStarted: (String url) {
- pageStarted.complete(null);
- },
- onPageFinished: (String url) {
- pageLoaded.complete(null);
- },
- initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
- ),
- ),
- );
- final WebViewController controller = await controllerCompleter.future;
- await pageStarted.future;
- await pageLoaded.future;
-
- String isPaused =
- await controller.runJavascriptReturningResult('isPaused();');
- expect(isPaused, _webviewBool(false));
-
- pageStarted = Completer<void>();
- pageLoaded = Completer<void>();
-
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: key,
- initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- onPageStarted: (String url) {
- pageStarted.complete(null);
- },
- onPageFinished: (String url) {
- pageLoaded.complete(null);
- },
- ),
- ),
- );
-
- await controller.reload();
-
- await pageStarted.future;
- await pageLoaded.future;
-
- isPaused = await controller.runJavascriptReturningResult('isPaused();');
- expect(isPaused, _webviewBool(false));
+ isPaused =
+ await controller.runJavaScriptReturningResult('isPaused();') as bool;
+ expect(isPaused, true);
});
});
@@ -756,39 +480,26 @@
''';
final String getTitleTestBase64 =
base64Encode(const Utf8Encoder().convert(getTitleTest));
- final Completer<void> pageStarted = Completer<void>();
final Completer<void> pageLoaded = Completer<void>();
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- initialUrl: 'data:text/html;charset=utf-8;base64,$getTitleTestBase64',
- javascriptMode: JavascriptMode.unrestricted,
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- onPageStarted: (String url) {
- pageStarted.complete(null);
- },
- onPageFinished: (String url) {
- pageLoaded.complete(null);
- },
- ),
- ),
- );
+ final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(NavigationDelegate(
+ onPageFinished: (_) => pageLoaded.complete(),
+ ))
+ ..loadRequest(
+ Uri.parse('data:text/html;charset=utf-8;base64,$getTitleTestBase64'),
+ );
- final WebViewController controller = await controllerCompleter.future;
- await pageStarted.future;
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+
await pageLoaded.future;
// On at least iOS, it does not appear to be guaranteed that the native
// code has the title when the page load completes. Execute some JavaScript
// before checking the title to ensure that the page has been fully parsed
// and processed.
- await controller.runJavascript('1;');
+ await controller.runJavaScript('1;');
final String? title = await controller.getTitle();
expect(title, 'Some title');
@@ -821,32 +532,22 @@
base64Encode(const Utf8Encoder().convert(scrollTestPage));
final Completer<void> pageLoaded = Completer<void>();
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
+ final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(NavigationDelegate(
+ onPageFinished: (_) => pageLoaded.complete(),
+ ))
+ ..loadRequest(Uri.parse(
+ 'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
+ ));
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- initialUrl:
- 'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- onPageFinished: (String url) {
- pageLoaded.complete(null);
- },
- ),
- ),
- );
+ await tester.pumpWidget(WebViewWidget(controller: controller));
- final WebViewController controller = await controllerCompleter.future;
await pageLoaded.future;
await tester.pumpAndSettle(const Duration(seconds: 3));
- int scrollPosX = await controller.getScrollX();
- int scrollPosY = await controller.getScrollY();
+ Offset scrollPos = await controller.getScrollPosition();
// Check scrollTo()
const int X_SCROLL = 123;
@@ -854,95 +555,51 @@
// Get the initial position; this ensures that scrollTo is actually
// changing something, but also gives the native view's scroll position
// time to settle.
- expect(scrollPosX, isNot(X_SCROLL));
- expect(scrollPosX, isNot(Y_SCROLL));
+ expect(scrollPos.dx, isNot(X_SCROLL));
+ expect(scrollPos.dy, isNot(Y_SCROLL));
await controller.scrollTo(X_SCROLL, Y_SCROLL);
- scrollPosX = await controller.getScrollX();
- scrollPosY = await controller.getScrollY();
- expect(scrollPosX, X_SCROLL);
- expect(scrollPosY, Y_SCROLL);
+ scrollPos = await controller.getScrollPosition();
+ expect(scrollPos.dx, X_SCROLL);
+ expect(scrollPos.dy, Y_SCROLL);
// Check scrollBy() (on top of scrollTo())
await controller.scrollBy(X_SCROLL, Y_SCROLL);
- scrollPosX = await controller.getScrollX();
- scrollPosY = await controller.getScrollY();
- expect(scrollPosX, X_SCROLL * 2);
- expect(scrollPosY, Y_SCROLL * 2);
+ scrollPos = await controller.getScrollPosition();
+ expect(scrollPos.dx, X_SCROLL * 2);
+ expect(scrollPos.dy, Y_SCROLL * 2);
});
});
- // Minimal end-to-end testing of the legacy Android implementation.
- group('AndroidWebView (virtual display)', () {
- setUpAll(() {
- WebView.platform = AndroidWebView();
- });
-
- tearDownAll(() {
- WebView.platform = null;
- });
-
- testWidgets('initialUrl', (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- final Completer<void> pageFinishedCompleter = Completer<void>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: primaryUrl,
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- onPageFinished: pageFinishedCompleter.complete,
- ),
- ),
- );
-
- final WebViewController controller = await controllerCompleter.future;
- await pageFinishedCompleter.future;
-
- final String? currentUrl = await controller.currentUrl();
- expect(currentUrl, primaryUrl);
- });
- }, skip: !Platform.isAndroid);
-
group('NavigationDelegate', () {
const String blankPage = '<!DOCTYPE html><head></head><body></body></html>';
final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,'
'${base64Encode(const Utf8Encoder().convert(blankPage))}';
testWidgets('can allow requests', (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- final StreamController<String> pageLoads =
- StreamController<String>.broadcast();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: blankPageEncoded,
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- navigationDelegate: (NavigationRequest request) {
- return (request.url.contains('youtube.com'))
- ? NavigationDecision.prevent
- : NavigationDecision.navigate;
- },
- onPageFinished: (String url) => pageLoads.add(url),
- ),
- ),
- );
+ Completer<void> pageLoaded = Completer<void>();
- await pageLoads.stream.first; // Wait for initial page load.
- final WebViewController controller = await controllerCompleter.future;
- await controller.runJavascript('location.href = "$secondaryUrl"');
+ final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(NavigationDelegate(
+ onPageFinished: (_) => pageLoaded.complete(),
+ onNavigationRequest: (NavigationRequest navigationRequest) {
+ return (navigationRequest.url.contains('youtube.com'))
+ ? NavigationDecision.prevent
+ : NavigationDecision.navigate;
+ },
+ ));
- await pageLoads.stream.first; // Wait for the next page load.
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+
+ controller.loadRequest(Uri.parse(blankPageEncoded));
+
+ await pageLoaded.future; // Wait for initial page load.
+
+ pageLoaded = Completer<void>();
+ await controller.runJavaScript('location.href = "$secondaryUrl"');
+ await pageLoaded.future; // Wait for the next page load.
+
final String? currentUrl = await controller.currentUrl();
expect(currentUrl, secondaryUrl);
});
@@ -951,30 +608,18 @@
final Completer<WebResourceError> errorCompleter =
Completer<WebResourceError>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: 'https://www.notawebsite..com',
- onWebResourceError: (WebResourceError error) {
- errorCompleter.complete(error);
- },
- ),
- ),
- );
+ final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(
+ NavigationDelegate(onWebResourceError: (WebResourceError error) {
+ errorCompleter.complete(error);
+ }))
+ ..loadRequest(Uri.parse('https://www.notawebsite..com'));
+
+ await tester.pumpWidget(WebViewWidget(controller: controller));
final WebResourceError error = await errorCompleter.future;
expect(error, isNotNull);
-
- if (Platform.isIOS) {
- expect(error.domain, isNotNull);
- expect(error.failingUrl, isNull);
- } else if (Platform.isAndroid) {
- expect(error.errorType, isNotNull);
- expect(error.failingUrl?.startsWith('https://www.notawebsite..com'),
- isTrue);
- }
});
testWidgets('onWebResourceError is not called with valid url',
@@ -983,190 +628,99 @@
Completer<WebResourceError>();
final Completer<void> pageFinishCompleter = Completer<void>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl:
- 'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+',
- onWebResourceError: (WebResourceError error) {
- errorCompleter.complete(error);
- },
- onPageFinished: (_) => pageFinishCompleter.complete(),
- ),
- ),
- );
+ final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(NavigationDelegate(
+ onPageFinished: (_) => pageFinishCompleter.complete(),
+ onWebResourceError: (WebResourceError error) {
+ errorCompleter.complete(error);
+ },
+ ))
+ ..loadRequest(
+ Uri.parse('data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+'),
+ );
+
+ await tester.pumpWidget(WebViewWidget(controller: controller));
expect(errorCompleter.future, doesNotComplete);
await pageFinishCompleter.future;
});
- testWidgets(
- 'onWebResourceError only called for main frame',
- (WidgetTester tester) async {
- const String iframeTest = '''
- <!DOCTYPE html>
- <html>
- <head>
- <title>WebResourceError test</title>
- </head>
- <body>
- <iframe src="https://notawebsite..com"></iframe>
- </body>
- </html>
- ''';
- final String iframeTestBase64 =
- base64Encode(const Utf8Encoder().convert(iframeTest));
-
- final Completer<WebResourceError> errorCompleter =
- Completer<WebResourceError>();
- final Completer<void> pageFinishCompleter = Completer<void>();
-
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl:
- 'data:text/html;charset=utf-8;base64,$iframeTestBase64',
- onWebResourceError: (WebResourceError error) {
- errorCompleter.complete(error);
- },
- onPageFinished: (_) => pageFinishCompleter.complete(),
- ),
- ),
- );
-
- expect(errorCompleter.future, doesNotComplete);
- await pageFinishCompleter.future;
- },
- );
-
testWidgets('can block requests', (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- final StreamController<String> pageLoads =
- StreamController<String>.broadcast();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: blankPageEncoded,
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- navigationDelegate: (NavigationRequest request) {
- return (request.url.contains('youtube.com'))
+ Completer<void> pageLoaded = Completer<void>();
+
+ final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(NavigationDelegate(
+ onPageFinished: (_) => pageLoaded.complete(),
+ onNavigationRequest: (NavigationRequest navigationRequest) {
+ return (navigationRequest.url.contains('youtube.com'))
? NavigationDecision.prevent
: NavigationDecision.navigate;
- },
- onPageFinished: (String url) => pageLoads.add(url),
- ),
- ),
- );
+ }));
- await pageLoads.stream.first; // Wait for initial page load.
- final WebViewController controller = await controllerCompleter.future;
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+
+ controller.loadRequest(Uri.parse(blankPageEncoded));
+
+ await pageLoaded.future; // Wait for initial page load.
+
+ pageLoaded = Completer<void>();
await controller
- .runJavascript('location.href = "https://www.youtube.com/"');
+ .runJavaScript('location.href = "https://www.youtube.com/"');
// There should never be any second page load, since our new URL is
// blocked. Still wait for a potential page change for some time in order
// to give the test a chance to fail.
- await pageLoads.stream.first
+ await pageLoaded.future
.timeout(const Duration(milliseconds: 500), onTimeout: () => '');
final String? currentUrl = await controller.currentUrl();
expect(currentUrl, isNot(contains('youtube.com')));
});
testWidgets('supports asynchronous decisions', (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- final StreamController<String> pageLoads =
- StreamController<String>.broadcast();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: blankPageEncoded,
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- navigationDelegate: (NavigationRequest request) async {
+ Completer<void> pageLoaded = Completer<void>();
+
+ final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(NavigationDelegate(
+ onPageFinished: (_) => pageLoaded.complete(),
+ onNavigationRequest: (NavigationRequest navigationRequest) async {
NavigationDecision decision = NavigationDecision.prevent;
decision = await Future<NavigationDecision>.delayed(
const Duration(milliseconds: 10),
() => NavigationDecision.navigate);
return decision;
- },
- onPageFinished: (String url) => pageLoads.add(url),
- ),
- ),
- );
+ }));
- await pageLoads.stream.first; // Wait for initial page load.
- final WebViewController controller = await controllerCompleter.future;
- await controller.runJavascript('location.href = "$secondaryUrl"');
+ await tester.pumpWidget(WebViewWidget(controller: controller));
- await pageLoads.stream.first; // Wait for second page to load.
+ controller.loadRequest(Uri.parse(blankPageEncoded));
+
+ await pageLoaded.future; // Wait for initial page load.
+
+ pageLoaded = Completer<void>();
+ await controller.runJavaScript('location.href = "$secondaryUrl"');
+ await pageLoaded.future; // Wait for second page to load.
+
final String? currentUrl = await controller.currentUrl();
expect(currentUrl, secondaryUrl);
});
});
- testWidgets('launches with gestureNavigationEnabled on iOS',
- (WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: SizedBox(
- width: 400,
- height: 300,
- child: WebView(
- key: GlobalKey(),
- initialUrl: primaryUrl,
- gestureNavigationEnabled: true,
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- ),
- ),
- ),
- );
- final WebViewController controller = await controllerCompleter.future;
- final String? currentUrl = await controller.currentUrl();
- expect(currentUrl, primaryUrl);
- });
-
testWidgets('target _blank opens in same window',
(WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
final Completer<void> pageLoaded = Completer<void>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- onPageFinished: (String url) {
- pageLoaded.complete(null);
- },
- ),
- ),
- );
- final WebViewController controller = await controllerCompleter.future;
- await controller.runJavascript('window.open("$primaryUrl", "_blank")');
+
+ final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(NavigationDelegate(
+ onPageFinished: (_) => pageLoaded.complete(),
+ ));
+
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+
+ await controller.runJavaScript('window.open("$primaryUrl", "_blank")');
await pageLoaded.future;
final String? currentUrl = await controller.currentUrl();
expect(currentUrl, primaryUrl);
@@ -1175,31 +729,22 @@
testWidgets(
'can open new window and go back',
(WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
Completer<void> pageLoaded = Completer<void>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- javascriptMode: JavascriptMode.unrestricted,
- onPageFinished: (String url) {
- pageLoaded.complete();
- },
- initialUrl: primaryUrl,
- ),
- ),
- );
- final WebViewController controller = await controllerCompleter.future;
+
+ final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(NavigationDelegate(
+ onPageFinished: (_) => pageLoaded.complete(),
+ ))
+ ..loadRequest(Uri.parse(primaryUrl));
+
+ await tester.pumpWidget(WebViewWidget(controller: controller));
+
expect(controller.currentUrl(), completion(primaryUrl));
await pageLoaded.future;
pageLoaded = Completer<void>();
- await controller.runJavascript('window.open("$secondaryUrl")');
+ await controller.runJavaScript('window.open("$secondaryUrl")');
await pageLoaded.future;
pageLoaded = Completer<void>();
expect(controller.currentUrl(), completion(secondaryUrl));
@@ -1212,46 +757,39 @@
);
testWidgets(
- 'clearCache should clear local storage',
+ 'clearLocalStorage',
(WidgetTester tester) async {
- final Completer<WebViewController> controllerCompleter =
- Completer<WebViewController>();
-
Completer<void> pageLoadCompleter = Completer<void>();
- await tester.pumpWidget(
- Directionality(
- textDirection: TextDirection.ltr,
- child: WebView(
- key: GlobalKey(),
- initialUrl: primaryUrl,
- javascriptMode: JavascriptMode.unrestricted,
- onPageFinished: (_) => pageLoadCompleter.complete(),
- onWebViewCreated: (WebViewController controller) {
- controllerCompleter.complete(controller);
- },
- ),
- ),
- );
+ final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(NavigationDelegate(
+ onPageFinished: (_) => pageLoadCompleter.complete(),
+ ))
+ ..loadRequest(Uri.parse(primaryUrl));
+
+ await tester.pumpWidget(WebViewWidget(controller: controller));
await pageLoadCompleter.future;
pageLoadCompleter = Completer<void>();
- final WebViewController controller = await controllerCompleter.future;
- await controller.runJavascript('localStorage.setItem("myCat", "Tom");');
- final String myCatItem = await controller.runJavascriptReturningResult(
+ await controller.runJavaScript('localStorage.setItem("myCat", "Tom");');
+ final String myCatItem = await controller.runJavaScriptReturningResult(
'localStorage.getItem("myCat");',
- );
- expect(myCatItem, _webviewString('Tom'));
+ ) as String;
+ expect(myCatItem, _webViewString('Tom'));
- await controller.clearCache();
+ await controller.clearLocalStorage();
+
+ // Reload page to have changes take effect.
+ await controller.reload();
await pageLoadCompleter.future;
late final String? nullItem;
try {
- nullItem = await controller.runJavascriptReturningResult(
+ nullItem = await controller.runJavaScriptReturningResult(
'localStorage.getItem("myCat");',
- );
+ ) as String;
} catch (exception) {
if (defaultTargetPlatform == TargetPlatform.iOS &&
exception is ArgumentError &&
@@ -1260,23 +798,14 @@
nullItem = '<null>';
}
}
- expect(nullItem, _webviewNull());
+ expect(nullItem, _webViewNull());
},
);
}
-// JavaScript booleans evaluate to different string values on Android and iOS.
-// This utility method returns the string boolean value of the current platform.
-String _webviewBool(bool value) {
- if (defaultTargetPlatform == TargetPlatform.iOS) {
- return value ? '1' : '0';
- }
- return value ? 'true' : 'false';
-}
-
// JavaScript `null` evaluate to different string values on Android and iOS.
// This utility method returns the string boolean value of the current platform.
-String _webviewNull() {
+String _webViewNull() {
if (defaultTargetPlatform == TargetPlatform.iOS) {
return '<null>';
}
@@ -1285,7 +814,7 @@
// JavaScript String evaluate to different string values on Android and iOS.
// This utility method returns the string boolean value of the current platform.
-String _webviewString(String value) {
+String _webViewString(String value) {
if (defaultTargetPlatform == TargetPlatform.iOS) {
return value;
}
@@ -1298,20 +827,24 @@
}
Future<String> _runJavascriptReturningResult(
- WebViewController controller, String js) async {
+ WebViewController controller,
+ String js,
+) async {
if (defaultTargetPlatform == TargetPlatform.iOS) {
- return controller.runJavascriptReturningResult(js);
+ return await controller.runJavaScriptReturningResult(js) as String;
}
- return jsonDecode(await controller.runJavascriptReturningResult(js))
+ return jsonDecode(await controller.runJavaScriptReturningResult(js) as String)
as String;
}
class ResizableWebView extends StatefulWidget {
- const ResizableWebView(
- {Key? key, required this.onResize, required this.onPageFinished})
- : super(key: key);
+ const ResizableWebView({
+ super.key,
+ required this.onResize,
+ required this.onPageFinished,
+ });
- final JavascriptMessageHandler onResize;
+ final VoidCallback onResize;
final VoidCallback onPageFinished;
@override
@@ -1319,6 +852,23 @@
}
class ResizableWebViewState extends State<ResizableWebView> {
+ late final WebViewController controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setNavigationDelegate(NavigationDelegate(
+ onPageFinished: (_) => widget.onPageFinished(),
+ ))
+ ..addJavaScriptChannel(
+ 'Resize',
+ onMessageReceived: (_) {
+ widget.onResize();
+ },
+ )
+ ..loadRequest(
+ Uri.parse(
+ 'data:text/html;charset=utf-8;base64,${base64Encode(const Utf8Encoder().convert(resizePage))}',
+ ),
+ );
+
double webViewWidth = 200;
double webViewHeight = 200;
@@ -1341,28 +891,14 @@
@override
Widget build(BuildContext context) {
- final String resizeTestBase64 =
- base64Encode(const Utf8Encoder().convert(resizePage));
return Directionality(
textDirection: TextDirection.ltr,
child: Column(
children: <Widget>[
SizedBox(
- width: webViewWidth,
- height: webViewHeight,
- child: WebView(
- initialUrl:
- 'data:text/html;charset=utf-8;base64,$resizeTestBase64',
- javascriptChannels: <JavascriptChannel>{
- JavascriptChannel(
- name: 'Resize',
- onMessageReceived: widget.onResize,
- ),
- },
- onPageFinished: (_) => widget.onPageFinished(),
- javascriptMode: JavascriptMode.unrestricted,
- ),
- ),
+ width: webViewWidth,
+ height: webViewHeight,
+ child: WebViewWidget(controller: controller)),
TextButton(
key: const Key('resizeButton'),
onPressed: () {
diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart
index 0e71290..239b417 100644
--- a/packages/webview_flutter/webview_flutter/example/lib/main.dart
+++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// ignore_for_file: public_member_api_docs, avoid_print
+// ignore_for_file: public_member_api_docs
import 'dart:async';
import 'dart:convert';
@@ -12,6 +12,12 @@
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:webview_flutter/webview_flutter.dart';
+// #docregion platform_imports
+// Import for Android features.
+import 'package:webview_flutter_android/webview_flutter_android.dart';
+// Import for iOS features.
+import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
+// #enddocregion platform_imports
void main() => runApp(const MaterialApp(home: WebViewExample()));
@@ -71,24 +77,86 @@
''';
class WebViewExample extends StatefulWidget {
- const WebViewExample({Key? key, this.cookieManager}) : super(key: key);
-
- final CookieManager? cookieManager;
+ const WebViewExample({super.key});
@override
State<WebViewExample> createState() => _WebViewExampleState();
}
class _WebViewExampleState extends State<WebViewExample> {
- final Completer<WebViewController> _controller =
- Completer<WebViewController>();
+ late final WebViewController _controller;
@override
void initState() {
super.initState();
- if (Platform.isAndroid) {
- WebView.platform = SurfaceAndroidWebView();
+
+ // #docregion platform_features
+ late final PlatformWebViewControllerCreationParams params;
+ if (WebViewPlatform.instance is WebKitWebViewPlatform) {
+ params = WebKitWebViewControllerCreationParams(
+ allowsInlineMediaPlayback: true,
+ mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
+ );
+ } else {
+ params = const PlatformWebViewControllerCreationParams();
}
+
+ final WebViewController controller =
+ WebViewController.fromPlatformCreationParams(params);
+ // #enddocregion platform_features
+
+ controller
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setBackgroundColor(const Color(0x00000000))
+ ..setNavigationDelegate(
+ NavigationDelegate(
+ onProgress: (int progress) {
+ debugPrint('WebView is loading (progress : $progress%)');
+ },
+ onPageStarted: (String url) {
+ debugPrint('Page started loading: $url');
+ },
+ onPageFinished: (String url) {
+ debugPrint('Page finished loading: $url');
+ },
+ onWebResourceError: (WebResourceError error) {
+ debugPrint('''
+Page resource error:
+ code: ${error.errorCode}
+ description: ${error.description}
+ errorType: ${error.errorType}
+ isForMainFrame: ${error.isForMainFrame}
+ ''');
+ },
+ onNavigationRequest: (NavigationRequest request) {
+ if (request.url.startsWith('https://www.youtube.com/')) {
+ debugPrint('blocking navigation to ${request.url}');
+ return NavigationDecision.prevent;
+ }
+ debugPrint('allowing navigation to ${request.url}');
+ return NavigationDecision.navigate;
+ },
+ ),
+ )
+ ..addJavaScriptChannel(
+ 'Toaster',
+ onMessageReceived: (JavaScriptMessage message) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(message.message)),
+ );
+ },
+ )
+ ..loadRequest(Uri.parse('https://flutter.dev'));
+
+ // #docregion platform_features
+ if (controller.platform is AndroidWebViewController) {
+ AndroidWebViewController.enableDebugging(true);
+ (controller.platform as AndroidWebViewController)
+ .setMediaPlaybackRequiresUserGesture(false);
+ }
+ // #enddocregion platform_features
+
+ _controller = controller;
}
@override
@@ -99,77 +167,25 @@
title: const Text('Flutter WebView example'),
// This drop down menu demonstrates that Flutter widgets can be shown over the web view.
actions: <Widget>[
- NavigationControls(_controller.future),
- SampleMenu(_controller.future, widget.cookieManager),
+ NavigationControls(webViewController: _controller),
+ SampleMenu(webViewController: _controller),
],
),
- body: WebView(
- initialUrl: 'https://flutter.dev',
- javascriptMode: JavascriptMode.unrestricted,
- onWebViewCreated: (WebViewController webViewController) {
- _controller.complete(webViewController);
- },
- onProgress: (int progress) {
- print('WebView is loading (progress : $progress%)');
- },
- javascriptChannels: <JavascriptChannel>{
- _toasterJavascriptChannel(context),
- },
- navigationDelegate: (NavigationRequest request) {
- if (request.url.startsWith('https://www.youtube.com/')) {
- print('blocking navigation to $request}');
- return NavigationDecision.prevent;
- }
- print('allowing navigation to $request');
- return NavigationDecision.navigate;
- },
- onPageStarted: (String url) {
- print('Page started loading: $url');
- },
- onPageFinished: (String url) {
- print('Page finished loading: $url');
- },
- gestureNavigationEnabled: true,
- backgroundColor: const Color(0x00000000),
- ),
+ body: WebViewWidget(controller: _controller),
floatingActionButton: favoriteButton(),
);
}
- JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
- return JavascriptChannel(
- name: 'Toaster',
- onMessageReceived: (JavascriptMessage message) {
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(content: Text(message.message)),
- );
- });
- }
-
Widget favoriteButton() {
- return FutureBuilder<WebViewController>(
- future: _controller.future,
- builder: (BuildContext context,
- AsyncSnapshot<WebViewController> controller) {
- return FloatingActionButton(
- onPressed: () async {
- String? url;
- if (controller.hasData) {
- url = await controller.data!.currentUrl();
- }
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(
- content: Text(
- controller.hasData
- ? 'Favorited $url'
- : 'Unable to favorite',
- ),
- ),
- );
- },
- child: const Icon(Icons.favorite),
- );
- });
+ return FloatingActionButton(
+ onPressed: () async {
+ final String? url = await _controller.currentUrl();
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text('Favorited $url')),
+ );
+ },
+ child: const Icon(Icons.favorite),
+ );
}
}
@@ -190,137 +206,130 @@
}
class SampleMenu extends StatelessWidget {
- SampleMenu(this.controller, CookieManager? cookieManager, {Key? key})
- : cookieManager = cookieManager ?? CookieManager(),
- super(key: key);
+ SampleMenu({
+ super.key,
+ required this.webViewController,
+ });
- final Future<WebViewController> controller;
- late final CookieManager cookieManager;
+ final WebViewController webViewController;
+ late final WebViewCookieManager cookieManager = WebViewCookieManager();
@override
Widget build(BuildContext context) {
- return FutureBuilder<WebViewController>(
- future: controller,
- builder:
- (BuildContext context, AsyncSnapshot<WebViewController> controller) {
- return PopupMenuButton<MenuOptions>(
- key: const ValueKey<String>('ShowPopupMenu'),
- onSelected: (MenuOptions value) {
- switch (value) {
- case MenuOptions.showUserAgent:
- _onShowUserAgent(controller.data!, context);
- break;
- case MenuOptions.listCookies:
- _onListCookies(controller.data!, context);
- break;
- case MenuOptions.clearCookies:
- _onClearCookies(context);
- break;
- case MenuOptions.addToCache:
- _onAddToCache(controller.data!, context);
- break;
- case MenuOptions.listCache:
- _onListCache(controller.data!, context);
- break;
- case MenuOptions.clearCache:
- _onClearCache(controller.data!, context);
- break;
- case MenuOptions.navigationDelegate:
- _onNavigationDelegateExample(controller.data!, context);
- break;
- case MenuOptions.doPostRequest:
- _onDoPostRequest(controller.data!, context);
- break;
- case MenuOptions.loadLocalFile:
- _onLoadLocalFileExample(controller.data!, context);
- break;
- case MenuOptions.loadFlutterAsset:
- _onLoadFlutterAssetExample(controller.data!, context);
- break;
- case MenuOptions.loadHtmlString:
- _onLoadHtmlStringExample(controller.data!, context);
- break;
- case MenuOptions.transparentBackground:
- _onTransparentBackground(controller.data!, context);
- break;
- case MenuOptions.setCookie:
- _onSetCookie(controller.data!, context);
- break;
- }
- },
- itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
- PopupMenuItem<MenuOptions>(
- value: MenuOptions.showUserAgent,
- enabled: controller.hasData,
- child: const Text('Show user agent'),
- ),
- const PopupMenuItem<MenuOptions>(
- value: MenuOptions.listCookies,
- child: Text('List cookies'),
- ),
- const PopupMenuItem<MenuOptions>(
- value: MenuOptions.clearCookies,
- child: Text('Clear cookies'),
- ),
- const PopupMenuItem<MenuOptions>(
- value: MenuOptions.addToCache,
- child: Text('Add to cache'),
- ),
- const PopupMenuItem<MenuOptions>(
- value: MenuOptions.listCache,
- child: Text('List cache'),
- ),
- const PopupMenuItem<MenuOptions>(
- value: MenuOptions.clearCache,
- child: Text('Clear cache'),
- ),
- const PopupMenuItem<MenuOptions>(
- value: MenuOptions.navigationDelegate,
- child: Text('Navigation Delegate example'),
- ),
- const PopupMenuItem<MenuOptions>(
- value: MenuOptions.doPostRequest,
- child: Text('Post Request'),
- ),
- const PopupMenuItem<MenuOptions>(
- value: MenuOptions.loadHtmlString,
- child: Text('Load HTML string'),
- ),
- const PopupMenuItem<MenuOptions>(
- value: MenuOptions.loadLocalFile,
- child: Text('Load local file'),
- ),
- const PopupMenuItem<MenuOptions>(
- value: MenuOptions.loadFlutterAsset,
- child: Text('Load Flutter Asset'),
- ),
- const PopupMenuItem<MenuOptions>(
- key: ValueKey<String>('ShowTransparentBackgroundExample'),
- value: MenuOptions.transparentBackground,
- child: Text('Transparent background example'),
- ),
- const PopupMenuItem<MenuOptions>(
- value: MenuOptions.setCookie,
- child: Text('Set cookie'),
- ),
- ],
- );
+ return PopupMenuButton<MenuOptions>(
+ key: const ValueKey<String>('ShowPopupMenu'),
+ onSelected: (MenuOptions value) {
+ switch (value) {
+ case MenuOptions.showUserAgent:
+ _onShowUserAgent();
+ break;
+ case MenuOptions.listCookies:
+ _onListCookies(context);
+ break;
+ case MenuOptions.clearCookies:
+ _onClearCookies(context);
+ break;
+ case MenuOptions.addToCache:
+ _onAddToCache(context);
+ break;
+ case MenuOptions.listCache:
+ _onListCache();
+ break;
+ case MenuOptions.clearCache:
+ _onClearCache(context);
+ break;
+ case MenuOptions.navigationDelegate:
+ _onNavigationDelegateExample();
+ break;
+ case MenuOptions.doPostRequest:
+ _onDoPostRequest();
+ break;
+ case MenuOptions.loadLocalFile:
+ _onLoadLocalFileExample();
+ break;
+ case MenuOptions.loadFlutterAsset:
+ _onLoadFlutterAssetExample();
+ break;
+ case MenuOptions.loadHtmlString:
+ _onLoadHtmlStringExample();
+ break;
+ case MenuOptions.transparentBackground:
+ _onTransparentBackground();
+ break;
+ case MenuOptions.setCookie:
+ _onSetCookie();
+ break;
+ }
},
+ itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
+ const PopupMenuItem<MenuOptions>(
+ value: MenuOptions.showUserAgent,
+ child: Text('Show user agent'),
+ ),
+ const PopupMenuItem<MenuOptions>(
+ value: MenuOptions.listCookies,
+ child: Text('List cookies'),
+ ),
+ const PopupMenuItem<MenuOptions>(
+ value: MenuOptions.clearCookies,
+ child: Text('Clear cookies'),
+ ),
+ const PopupMenuItem<MenuOptions>(
+ value: MenuOptions.addToCache,
+ child: Text('Add to cache'),
+ ),
+ const PopupMenuItem<MenuOptions>(
+ value: MenuOptions.listCache,
+ child: Text('List cache'),
+ ),
+ const PopupMenuItem<MenuOptions>(
+ value: MenuOptions.clearCache,
+ child: Text('Clear cache'),
+ ),
+ const PopupMenuItem<MenuOptions>(
+ value: MenuOptions.navigationDelegate,
+ child: Text('Navigation Delegate example'),
+ ),
+ const PopupMenuItem<MenuOptions>(
+ value: MenuOptions.doPostRequest,
+ child: Text('Post Request'),
+ ),
+ const PopupMenuItem<MenuOptions>(
+ value: MenuOptions.loadHtmlString,
+ child: Text('Load HTML string'),
+ ),
+ const PopupMenuItem<MenuOptions>(
+ value: MenuOptions.loadLocalFile,
+ child: Text('Load local file'),
+ ),
+ const PopupMenuItem<MenuOptions>(
+ value: MenuOptions.loadFlutterAsset,
+ child: Text('Load Flutter Asset'),
+ ),
+ const PopupMenuItem<MenuOptions>(
+ key: ValueKey<String>('ShowTransparentBackgroundExample'),
+ value: MenuOptions.transparentBackground,
+ child: Text('Transparent background example'),
+ ),
+ const PopupMenuItem<MenuOptions>(
+ value: MenuOptions.setCookie,
+ child: Text('Set cookie'),
+ ),
+ ],
);
}
- Future<void> _onShowUserAgent(
- WebViewController controller, BuildContext context) async {
+ Future<void> _onShowUserAgent() {
// Send a message with the user agent string to the Toaster JavaScript channel we registered
// with the WebView.
- await controller.runJavascript(
- 'Toaster.postMessage("User Agent: " + navigator.userAgent);');
+ return webViewController.runJavaScript(
+ 'Toaster.postMessage("User Agent: " + navigator.userAgent);',
+ );
}
- Future<void> _onListCookies(
- WebViewController controller, BuildContext context) async {
- final String cookies =
- await controller.runJavascriptReturningResult('document.cookie');
+ Future<void> _onListCookies(BuildContext context) async {
+ final String cookies = await webViewController
+ .runJavaScriptReturningResult('document.cookie') as String;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Column(
mainAxisAlignment: MainAxisAlignment.end,
@@ -333,26 +342,25 @@
));
}
- Future<void> _onAddToCache(
- WebViewController controller, BuildContext context) async {
- await controller.runJavascript(
- 'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";');
+ Future<void> _onAddToCache(BuildContext context) async {
+ await webViewController.runJavaScript(
+ 'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";',
+ );
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('Added a test entry to cache.'),
));
}
- Future<void> _onListCache(
- WebViewController controller, BuildContext context) async {
- await controller.runJavascript('caches.keys()'
+ Future<void> _onListCache() {
+ return webViewController.runJavaScript('caches.keys()'
// ignore: missing_whitespace_between_adjacent_strings
'.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
'.then((caches) => Toaster.postMessage(caches))');
}
- Future<void> _onClearCache(
- WebViewController controller, BuildContext context) async {
- await controller.clearCache();
+ Future<void> _onClearCache(BuildContext context) async {
+ await webViewController.clearCache();
+ await webViewController.clearLocalStorage();
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('Cache cleared.'),
));
@@ -369,53 +377,53 @@
));
}
- Future<void> _onNavigationDelegateExample(
- WebViewController controller, BuildContext context) async {
- final String contentBase64 =
- base64Encode(const Utf8Encoder().convert(kNavigationExamplePage));
- await controller.loadUrl('data:text/html;base64,$contentBase64');
+ Future<void> _onNavigationDelegateExample() {
+ final String contentBase64 = base64Encode(
+ const Utf8Encoder().convert(kNavigationExamplePage),
+ );
+ return webViewController.loadRequest(
+ Uri.parse('data:text/html;base64,$contentBase64'),
+ );
}
- Future<void> _onSetCookie(
- WebViewController controller, BuildContext context) async {
+ Future<void> _onSetCookie() async {
await cookieManager.setCookie(
const WebViewCookie(
- name: 'foo', value: 'bar', domain: 'httpbin.org', path: '/anything'),
+ name: 'foo',
+ value: 'bar',
+ domain: 'httpbin.org',
+ path: '/anything',
+ ),
);
- await controller.loadUrl('https://httpbin.org/anything');
+ await webViewController.loadRequest(Uri.parse(
+ 'https://httpbin.org/anything',
+ ));
}
- Future<void> _onDoPostRequest(
- WebViewController controller, BuildContext context) async {
- final WebViewRequest request = WebViewRequest(
- uri: Uri.parse('https://httpbin.org/post'),
- method: WebViewRequestMethod.post,
+ Future<void> _onDoPostRequest() {
+ return webViewController.loadRequest(
+ Uri.parse('https://httpbin.org/post'),
+ method: LoadRequestMethod.post,
headers: <String, String>{'foo': 'bar', 'Content-Type': 'text/plain'},
body: Uint8List.fromList('Test Body'.codeUnits),
);
- await controller.loadRequest(request);
}
- Future<void> _onLoadLocalFileExample(
- WebViewController controller, BuildContext context) async {
+ Future<void> _onLoadLocalFileExample() async {
final String pathToIndex = await _prepareLocalFile();
-
- await controller.loadFile(pathToIndex);
+ await webViewController.loadFile(pathToIndex);
}
- Future<void> _onLoadFlutterAssetExample(
- WebViewController controller, BuildContext context) async {
- await controller.loadFlutterAsset('assets/www/index.html');
+ Future<void> _onLoadFlutterAssetExample() {
+ return webViewController.loadFlutterAsset('assets/www/index.html');
}
- Future<void> _onLoadHtmlStringExample(
- WebViewController controller, BuildContext context) async {
- await controller.loadHtmlString(kLocalExamplePage);
+ Future<void> _onLoadHtmlStringExample() {
+ return webViewController.loadHtmlString(kLocalExamplePage);
}
- Future<void> _onTransparentBackground(
- WebViewController controller, BuildContext context) async {
- await controller.loadHtmlString(kTransparentBackgroundPage);
+ Future<void> _onTransparentBackground() {
+ return webViewController.loadHtmlString(kTransparentBackgroundPage);
}
Widget _getCookieList(String cookies) {
@@ -445,65 +453,45 @@
}
class NavigationControls extends StatelessWidget {
- const NavigationControls(this._webViewControllerFuture, {Key? key})
- : assert(_webViewControllerFuture != null),
- super(key: key);
+ const NavigationControls({super.key, required this.webViewController});
- final Future<WebViewController> _webViewControllerFuture;
+ final WebViewController webViewController;
@override
Widget build(BuildContext context) {
- return FutureBuilder<WebViewController>(
- future: _webViewControllerFuture,
- builder:
- (BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
- final bool webViewReady =
- snapshot.connectionState == ConnectionState.done;
- final WebViewController? controller = snapshot.data;
- return Row(
- children: <Widget>[
- IconButton(
- icon: const Icon(Icons.arrow_back_ios),
- onPressed: !webViewReady
- ? null
- : () async {
- if (await controller!.canGoBack()) {
- await controller.goBack();
- } else {
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(content: Text('No back history item')),
- );
- return;
- }
- },
- ),
- IconButton(
- icon: const Icon(Icons.arrow_forward_ios),
- onPressed: !webViewReady
- ? null
- : () async {
- if (await controller!.canGoForward()) {
- await controller.goForward();
- } else {
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(
- content: Text('No forward history item')),
- );
- return;
- }
- },
- ),
- IconButton(
- icon: const Icon(Icons.replay),
- onPressed: !webViewReady
- ? null
- : () {
- controller!.reload();
- },
- ),
- ],
- );
- },
+ return Row(
+ children: <Widget>[
+ IconButton(
+ icon: const Icon(Icons.arrow_back_ios),
+ onPressed: () async {
+ if (await webViewController.canGoBack()) {
+ await webViewController.goBack();
+ } else {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('No back history item')),
+ );
+ return;
+ }
+ },
+ ),
+ IconButton(
+ icon: const Icon(Icons.arrow_forward_ios),
+ onPressed: () async {
+ if (await webViewController.canGoForward()) {
+ await webViewController.goForward();
+ } else {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('No forward history item')),
+ );
+ return;
+ }
+ },
+ ),
+ IconButton(
+ icon: const Icon(Icons.replay),
+ onPressed: () => webViewController.reload(),
+ ),
+ ],
);
}
}
diff --git a/packages/webview_flutter/webview_flutter/example/lib/simple_example.dart b/packages/webview_flutter/webview_flutter/example/lib/simple_example.dart
new file mode 100644
index 0000000..dfee9e6
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter/example/lib/simple_example.dart
@@ -0,0 +1,59 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+import 'package:webview_flutter/webview_flutter.dart';
+
+void main() => runApp(const MaterialApp(home: WebViewExample()));
+
+class WebViewExample extends StatefulWidget {
+ const WebViewExample({super.key});
+
+ @override
+ State<WebViewExample> createState() => _WebViewExampleState();
+}
+
+class _WebViewExampleState extends State<WebViewExample> {
+ late final WebViewController controller;
+
+ @override
+ void initState() {
+ super.initState();
+
+ // #docregion webview_controller
+ controller = WebViewController()
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
+ ..setBackgroundColor(const Color(0x00000000))
+ ..setNavigationDelegate(
+ NavigationDelegate(
+ onProgress: (int progress) {
+ // Update loading bar.
+ },
+ onPageStarted: (String url) {},
+ onPageFinished: (String url) {},
+ onWebResourceError: (WebResourceError error) {},
+ onNavigationRequest: (NavigationRequest request) {
+ if (request.url.startsWith('https://www.youtube.com/')) {
+ return NavigationDecision.prevent;
+ }
+ return NavigationDecision.navigate;
+ },
+ ),
+ )
+ ..loadRequest(Uri.parse('https://flutter.dev'));
+ // #enddocregion webview_controller
+ }
+
+ // #docregion webview_widget
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: const Text('Flutter Simple Example')),
+ body: WebViewWidget(controller: controller),
+ );
+ }
+ // #enddocregion webview_widget
+}
diff --git a/packages/webview_flutter/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/webview_flutter/example/pubspec.yaml
index 6b01b53..4d8d788 100644
--- a/packages/webview_flutter/webview_flutter/example/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter/example/pubspec.yaml
@@ -3,8 +3,8 @@
publish_to: none
environment:
- sdk: ">=2.14.0 <3.0.0"
- flutter: ">=2.10.0"
+ sdk: ">=2.17.0 <3.0.0"
+ flutter: ">=3.0.0"
dependencies:
flutter:
@@ -17,8 +17,11 @@
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
+ webview_flutter_android: ^3.0.0
+ webview_flutter_wkwebview: ^3.0.0
dev_dependencies:
+ build_runner: ^2.1.5
espresso: ^0.2.0
flutter_driver:
sdk: flutter
@@ -26,6 +29,7 @@
sdk: flutter
integration_test:
sdk: flutter
+ webview_flutter_platform_interface: ^2.0.0
flutter:
uses-material-design: true
diff --git a/packages/webview_flutter/webview_flutter/example/test/main_test.dart b/packages/webview_flutter/webview_flutter/example/test/main_test.dart
index 8676333..7857022 100644
--- a/packages/webview_flutter/webview_flutter/example/test/main_test.dart
+++ b/packages/webview_flutter/webview_flutter/example/test/main_test.dart
@@ -4,17 +4,17 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
-import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_flutter_example/main.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
void main() {
+ setUp(() {
+ WebViewPlatform.instance = FakeWebViewPlatform();
+ });
+
testWidgets('Test snackbar from ScaffoldMessenger',
(WidgetTester tester) async {
- await tester.pumpWidget(
- MaterialApp(
- home: WebViewExample(cookieManager: FakeCookieManager()),
- ),
- );
+ await tester.pumpWidget(const MaterialApp(home: WebViewExample()));
expect(find.byIcon(Icons.favorite), findsOneWidget);
await tester.tap(find.byIcon(Icons.favorite));
await tester.pump();
@@ -22,18 +22,95 @@
});
}
-class FakeCookieManager implements CookieManager {
- factory FakeCookieManager() {
- return _instance ??= FakeCookieManager._();
+class FakeWebViewPlatform extends WebViewPlatform {
+ @override
+ PlatformWebViewController createPlatformWebViewController(
+ PlatformWebViewControllerCreationParams params,
+ ) {
+ return FakeWebViewController(params);
}
- FakeCookieManager._();
-
- static FakeCookieManager? _instance;
+ @override
+ PlatformWebViewWidget createPlatformWebViewWidget(
+ PlatformWebViewWidgetCreationParams params,
+ ) {
+ return FakeWebViewWidget(params);
+ }
@override
- Future<bool> clearCookies() => throw UnimplementedError();
+ PlatformWebViewCookieManager createPlatformCookieManager(
+ PlatformWebViewCookieManagerCreationParams params,
+ ) {
+ return FakeCookieManager(params);
+ }
@override
- Future<void> setCookie(WebViewCookie cookie) => throw UnimplementedError();
+ PlatformNavigationDelegate createPlatformNavigationDelegate(
+ PlatformNavigationDelegateCreationParams params,
+ ) {
+ return FakeNavigationDelegate(params);
+ }
+}
+
+class FakeWebViewController extends PlatformWebViewController {
+ FakeWebViewController(super.params) : super.implementation();
+
+ @override
+ Future<void> setJavaScriptMode(JavaScriptMode javaScriptMode) async {}
+
+ @override
+ Future<void> setBackgroundColor(Color color) async {}
+
+ @override
+ Future<void> setPlatformNavigationDelegate(
+ PlatformNavigationDelegate handler,
+ ) async {}
+
+ @override
+ Future<void> addJavaScriptChannel(
+ JavaScriptChannelParams javaScriptChannelParams) async {}
+
+ @override
+ Future<void> loadRequest(LoadRequestParams params) async {}
+
+ @override
+ Future<String?> currentUrl() async {
+ return 'https://www.google.com';
+ }
+}
+
+class FakeCookieManager extends PlatformWebViewCookieManager {
+ FakeCookieManager(super.params) : super.implementation();
+}
+
+class FakeWebViewWidget extends PlatformWebViewWidget {
+ FakeWebViewWidget(super.params) : super.implementation();
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}
+
+class FakeNavigationDelegate extends PlatformNavigationDelegate {
+ FakeNavigationDelegate(super.params) : super.implementation();
+
+ @override
+ Future<void> setOnNavigationRequest(
+ NavigationRequestCallback onNavigationRequest,
+ ) async {}
+
+ @override
+ Future<void> setOnPageFinished(PageEventCallback onPageFinished) async {}
+
+ @override
+ Future<void> setOnPageStarted(PageEventCallback onPageStarted) async {}
+
+ @override
+ Future<void> setOnProgress(ProgressCallback onProgress) async {}
+
+ @override
+ Future<void> setOnWebResourceError(
+ WebResourceErrorCallback onWebResourceError,
+ ) async {}
}
diff --git a/packages/webview_flutter/webview_flutter/lib/platform_interface.dart b/packages/webview_flutter/webview_flutter/lib/src/legacy/platform_interface.dart
similarity index 89%
rename from packages/webview_flutter/webview_flutter/lib/platform_interface.dart
rename to packages/webview_flutter/webview_flutter/lib/src/legacy/platform_interface.dart
index 48f7434..e036d2e 100644
--- a/packages/webview_flutter/webview_flutter/lib/platform_interface.dart
+++ b/packages/webview_flutter/webview_flutter/lib/src/legacy/platform_interface.dart
@@ -5,7 +5,7 @@
/// Re-export the classes from the webview_flutter_platform_interface through
/// the `platform_interface.dart` file so we don't accidentally break any
/// non-endorsed existing implementations of the interface.
-export 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'
+export 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart'
show
AutoMediaPlaybackPolicy,
CreationParams,
diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview.dart b/packages/webview_flutter/webview_flutter/lib/src/legacy/webview.dart
similarity index 98%
rename from packages/webview_flutter/webview_flutter/lib/src/webview.dart
rename to packages/webview_flutter/webview_flutter/lib/src/legacy/webview.dart
index 7de8e28..8d7baa9 100644
--- a/packages/webview_flutter/webview_flutter/lib/src/webview.dart
+++ b/packages/webview_flutter/webview_flutter/lib/src/legacy/webview.dart
@@ -8,10 +8,12 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
-import 'package:webview_flutter_android/webview_android_cookie_manager.dart';
-import 'package:webview_flutter_android/webview_surface_android.dart';
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
-import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
+// ignore: implementation_imports
+import 'package:webview_flutter_android/src/webview_flutter_android_legacy.dart';
+// ignore: implementation_imports
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart';
+// ignore: implementation_imports
+import 'package:webview_flutter_wkwebview/src/webview_flutter_wkwebview_legacy.dart';
/// Optional callback invoked when a web view is first created. [controller] is
/// the [WebViewController] for the created web view.
@@ -76,7 +78,7 @@
///
/// The `javascriptMode` and `autoMediaPlaybackPolicy` parameters must not be null.
const WebView({
- Key? key,
+ super.key,
this.onWebViewCreated,
this.initialUrl,
this.initialCookies = const <WebViewCookie>[],
@@ -98,8 +100,7 @@
this.backgroundColor,
}) : assert(javascriptMode != null),
assert(initialMediaPlaybackPolicy != null),
- assert(allowsInlineMediaPlayback != null),
- super(key: key);
+ assert(allowsInlineMediaPlayback != null);
static WebViewPlatform? _platform;
diff --git a/packages/webview_flutter/webview_flutter/lib/src/navigation_delegate.dart b/packages/webview_flutter/webview_flutter/lib/src/navigation_delegate.dart
new file mode 100644
index 0000000..0651ad4
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter/lib/src/navigation_delegate.dart
@@ -0,0 +1,104 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+
+import 'webview_controller.dart';
+
+/// Callbacks for accepting or rejecting navigation changes, and for tracking
+/// the progress of navigation requests.
+///
+/// See [WebViewController.setNavigationDelegate].
+class NavigationDelegate {
+ /// Constructs a [NavigationDelegate].
+ NavigationDelegate({
+ FutureOr<NavigationDecision> Function(NavigationRequest request)?
+ onNavigationRequest,
+ void Function(String url)? onPageStarted,
+ void Function(String url)? onPageFinished,
+ void Function(int progress)? onProgress,
+ void Function(WebResourceError error)? onWebResourceError,
+ }) : this.fromPlatformCreationParams(
+ const PlatformNavigationDelegateCreationParams(),
+ onNavigationRequest: onNavigationRequest,
+ onPageStarted: onPageStarted,
+ onPageFinished: onPageFinished,
+ onProgress: onProgress,
+ onWebResourceError: onWebResourceError,
+ );
+
+ /// Constructs a [NavigationDelegate] from creation params for a specific
+ /// platform.
+ NavigationDelegate.fromPlatformCreationParams(
+ PlatformNavigationDelegateCreationParams params, {
+ FutureOr<NavigationDecision> Function(NavigationRequest request)?
+ onNavigationRequest,
+ void Function(String url)? onPageStarted,
+ void Function(String url)? onPageFinished,
+ void Function(int progress)? onProgress,
+ void Function(WebResourceError error)? onWebResourceError,
+ }) : this.fromPlatform(
+ PlatformNavigationDelegate(params),
+ onNavigationRequest: onNavigationRequest,
+ onPageStarted: onPageStarted,
+ onPageFinished: onPageFinished,
+ onProgress: onProgress,
+ onWebResourceError: onWebResourceError,
+ );
+
+ /// Constructs a [NavigationDelegate] from a specific platform implementation.
+ NavigationDelegate.fromPlatform(
+ this.platform, {
+ this.onNavigationRequest,
+ this.onPageStarted,
+ this.onPageFinished,
+ this.onProgress,
+ this.onWebResourceError,
+ }) {
+ if (onNavigationRequest != null) {
+ platform.setOnNavigationRequest(onNavigationRequest!);
+ }
+ if (onPageStarted != null) {
+ platform.setOnPageStarted(onPageStarted!);
+ }
+ if (onPageFinished != null) {
+ platform.setOnPageFinished(onPageFinished!);
+ }
+ if (onProgress != null) {
+ platform.setOnProgress(onProgress!);
+ }
+ if (onWebResourceError != null) {
+ platform.setOnWebResourceError(onWebResourceError!);
+ }
+ }
+
+ /// Implementation of [PlatformNavigationDelegate] for the current platform.
+ final PlatformNavigationDelegate platform;
+
+ /// Invoked when a decision for a navigation request is pending.
+ ///
+ /// When a navigation is initiated by the WebView (e.g when a user clicks a
+ /// link) this delegate is called and has to decide how to proceed with the
+ /// navigation.
+ ///
+ /// *Important*: Some platforms may also trigger this callback from calls to
+ /// [WebViewController.loadRequest].
+ ///
+ /// See [NavigationDecision].
+ final NavigationRequestCallback? onNavigationRequest;
+
+ /// Invoked when a page has started loading.
+ final PageEventCallback? onPageStarted;
+
+ /// Invoked when a page has finished loading.
+ final PageEventCallback? onPageFinished;
+
+ /// Invoked when a page is loading to report the progress.
+ final ProgressCallback? onProgress;
+
+ /// Invoked when a resource loading error occurred.
+ final WebResourceErrorCallback? onWebResourceError;
+}
diff --git a/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart b/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart
deleted file mode 100644
index f4a0b20..0000000
--- a/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-library webview_flutter;
-
-export 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'
- show JavaScriptMessage, LoadRequestMethod, WebViewCookie;
-
-export 'src/webview_controller.dart';
-export 'src/webview_cookie_manager.dart';
-export 'src/webview_widget.dart';
diff --git a/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_controller.dart b/packages/webview_flutter/webview_flutter/lib/src/webview_controller.dart
similarity index 94%
rename from packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_controller.dart
rename to packages/webview_flutter/webview_flutter/lib/src/webview_controller.dart
index bd03b24..d632d1e 100644
--- a/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_controller.dart
+++ b/packages/webview_flutter/webview_flutter/lib/src/webview_controller.dart
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'dart:math';
-
// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231)
// ignore: unnecessary_import
import 'dart:typed_data';
import 'package:flutter/material.dart';
-import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+
+import 'navigation_delegate.dart';
/// Controls a WebView provided by the host platform.
///
@@ -125,6 +125,12 @@
return platform.reload();
}
+ /// Sets the [NavigationDelegate] containing the callback methods that are
+ /// called during navigation events.
+ Future<void> setNavigationDelegate(NavigationDelegate delegate) {
+ return platform.setPlatformNavigationDelegate(delegate.platform);
+ }
+
/// Clears all caches used by the WebView.
///
/// The following caches are cleared:
@@ -233,9 +239,8 @@
/// Returns the current scroll position of this view.
///
/// Scroll position is measured from the top left.
- Future<Offset> getScrollPosition() async {
- final Point<int> position = await platform.getScrollPosition();
- return Offset(position.x.toDouble(), position.y.toDouble());
+ Future<Offset> getScrollPosition() {
+ return platform.getScrollPosition();
}
/// Whether to support zooming using the on-screen zoom controls and gestures.
diff --git a/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_cookie_manager.dart b/packages/webview_flutter/webview_flutter/lib/src/webview_cookie_manager.dart
similarity index 93%
rename from packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_cookie_manager.dart
rename to packages/webview_flutter/webview_flutter/lib/src/webview_cookie_manager.dart
index a1091fa..bffa1b5 100644
--- a/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_cookie_manager.dart
+++ b/packages/webview_flutter/webview_flutter/lib/src/webview_cookie_manager.dart
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
/// Manages cookies pertaining to all WebViews.
class WebViewCookieManager {
diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview_flutter_legacy.dart b/packages/webview_flutter/webview_flutter/lib/src/webview_flutter_legacy.dart
new file mode 100644
index 0000000..d040fc2
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter/lib/src/webview_flutter_legacy.dart
@@ -0,0 +1,9 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+export 'package:webview_flutter_android/src/webview_flutter_android_legacy.dart';
+export 'package:webview_flutter_wkwebview/src/webview_flutter_wkwebview_legacy.dart';
+
+export 'legacy/platform_interface.dart';
+export 'legacy/webview.dart';
diff --git a/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_widget.dart b/packages/webview_flutter/webview_flutter/lib/src/webview_widget.dart
similarity index 92%
rename from packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_widget.dart
rename to packages/webview_flutter/webview_flutter/lib/src/webview_widget.dart
index 06e4f78..b318011 100644
--- a/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_widget.dart
+++ b/packages/webview_flutter/webview_flutter/lib/src/webview_widget.dart
@@ -5,7 +5,7 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
-import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
import 'webview_controller.dart';
@@ -35,8 +35,7 @@
}) : this.fromPlatform(key: key, platform: PlatformWebViewWidget(params));
/// Constructs a [WebViewWidget] from a specific platform implementation.
- WebViewWidget.fromPlatform({Key? key, required this.platform})
- : super(key: key);
+ WebViewWidget.fromPlatform({super.key, required this.platform});
/// Implementation of [PlatformWebViewWidget] for the current platform.
final PlatformWebViewWidget platform;
diff --git a/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart
index ba38771..7b8301d 100644
--- a/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart
+++ b/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart
@@ -2,9 +2,28 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-export 'package:webview_flutter_android/webview_android.dart';
-export 'package:webview_flutter_android/webview_surface_android.dart';
-export 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
+library webview_flutter;
-export 'platform_interface.dart';
-export 'src/webview.dart';
+export 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'
+ show
+ JavaScriptMessage,
+ JavaScriptMode,
+ LoadRequestMethod,
+ NavigationDecision,
+ NavigationRequest,
+ NavigationRequestCallback,
+ PageEventCallback,
+ PlatformNavigationDelegateCreationParams,
+ PlatformWebViewControllerCreationParams,
+ PlatformWebViewCookieManagerCreationParams,
+ PlatformWebViewWidgetCreationParams,
+ ProgressCallback,
+ WebResourceError,
+ WebResourceErrorCallback,
+ WebViewCookie,
+ WebViewPlatform;
+
+export 'src/navigation_delegate.dart';
+export 'src/webview_controller.dart';
+export 'src/webview_cookie_manager.dart';
+export 'src/webview_widget.dart';
diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml
index a02b032..58c0411 100644
--- a/packages/webview_flutter/webview_flutter/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter/pubspec.yaml
@@ -2,11 +2,11 @@
description: A Flutter plugin that provides a WebView widget on Android and iOS.
repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
-version: 3.0.4
+version: 4.0.0
environment:
- sdk: ">=2.14.0 <3.0.0"
- flutter: ">=2.10.0"
+ sdk: ">=2.17.0 <3.0.0"
+ flutter: ">=3.0.0"
flutter:
plugin:
@@ -19,9 +19,9 @@
dependencies:
flutter:
sdk: flutter
- webview_flutter_android: ^2.8.0
- webview_flutter_platform_interface: ^1.9.3
- webview_flutter_wkwebview: ^2.7.0
+ webview_flutter_android: ^3.0.0
+ webview_flutter_platform_interface: ^2.0.0
+ webview_flutter_wkwebview: ^3.0.0
dev_dependencies:
build_runner: ^2.1.5
@@ -29,4 +29,5 @@
sdk: flutter
flutter_test:
sdk: flutter
- mockito: ^5.0.16
+ mockito: ^5.3.2
+ plugin_platform_interface: ^2.1.3
diff --git a/packages/webview_flutter/webview_flutter/test/legacy/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/test/legacy/webview_flutter_test.dart
new file mode 100644
index 0000000..4db7011
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter/test/legacy/webview_flutter_test.dart
@@ -0,0 +1,1367 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:typed_data';
+
+import 'package:flutter/src/foundation/basic_types.dart';
+import 'package:flutter/src/gestures/recognizer.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+import 'package:webview_flutter/src/webview_flutter_legacy.dart';
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart';
+
+import 'webview_flutter_test.mocks.dart';
+
+typedef VoidCallback = void Function();
+
+@GenerateMocks(<Type>[WebViewPlatform, WebViewPlatformController])
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ late MockWebViewPlatform mockWebViewPlatform;
+ late MockWebViewPlatformController mockWebViewPlatformController;
+ late MockWebViewCookieManagerPlatform mockWebViewCookieManagerPlatform;
+
+ setUp(() {
+ mockWebViewPlatformController = MockWebViewPlatformController();
+ mockWebViewPlatform = MockWebViewPlatform();
+ mockWebViewCookieManagerPlatform = MockWebViewCookieManagerPlatform();
+ when(mockWebViewPlatform.build(
+ context: anyNamed('context'),
+ creationParams: anyNamed('creationParams'),
+ webViewPlatformCallbacksHandler:
+ anyNamed('webViewPlatformCallbacksHandler'),
+ javascriptChannelRegistry: anyNamed('javascriptChannelRegistry'),
+ onWebViewPlatformCreated: anyNamed('onWebViewPlatformCreated'),
+ gestureRecognizers: anyNamed('gestureRecognizers'),
+ )).thenAnswer((Invocation invocation) {
+ final WebViewPlatformCreatedCallback onWebViewPlatformCreated =
+ invocation.namedArguments[const Symbol('onWebViewPlatformCreated')]
+ as WebViewPlatformCreatedCallback;
+ return TestPlatformWebView(
+ mockWebViewPlatformController: mockWebViewPlatformController,
+ onWebViewPlatformCreated: onWebViewPlatformCreated,
+ );
+ });
+
+ WebView.platform = mockWebViewPlatform;
+ WebViewCookieManagerPlatform.instance = mockWebViewCookieManagerPlatform;
+ });
+
+ tearDown(() {
+ mockWebViewCookieManagerPlatform.reset();
+ });
+
+ testWidgets('Create WebView', (WidgetTester tester) async {
+ await tester.pumpWidget(const WebView());
+ });
+
+ testWidgets('Initial url', (WidgetTester tester) async {
+ await tester.pumpWidget(const WebView(initialUrl: 'https://youtube.com'));
+
+ final CreationParams params = captureBuildArgs(
+ mockWebViewPlatform,
+ creationParams: true,
+ ).single as CreationParams;
+
+ expect(params.initialUrl, 'https://youtube.com');
+ });
+
+ testWidgets('Javascript mode', (WidgetTester tester) async {
+ await tester.pumpWidget(const WebView(
+ javascriptMode: JavascriptMode.unrestricted,
+ ));
+
+ final CreationParams unrestrictedparams = captureBuildArgs(
+ mockWebViewPlatform,
+ creationParams: true,
+ ).single as CreationParams;
+
+ expect(
+ unrestrictedparams.webSettings!.javascriptMode,
+ JavascriptMode.unrestricted,
+ );
+
+ await tester.pumpWidget(const WebView());
+
+ final CreationParams disabledparams = captureBuildArgs(
+ mockWebViewPlatform,
+ creationParams: true,
+ ).single as CreationParams;
+
+ expect(disabledparams.webSettings!.javascriptMode, JavascriptMode.disabled);
+ });
+
+ testWidgets('Load file', (WidgetTester tester) async {
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+
+ await controller!.loadFile('/test/path/index.html');
+
+ verify(mockWebViewPlatformController.loadFile(
+ '/test/path/index.html',
+ ));
+ });
+
+ testWidgets('Load file with empty path', (WidgetTester tester) async {
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+
+ expect(() => controller!.loadFile(''), throwsAssertionError);
+ });
+
+ testWidgets('Load Flutter asset', (WidgetTester tester) async {
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+
+ await controller!.loadFlutterAsset('assets/index.html');
+
+ verify(mockWebViewPlatformController.loadFlutterAsset(
+ 'assets/index.html',
+ ));
+ });
+
+ testWidgets('Load Flutter asset with empty key', (WidgetTester tester) async {
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+
+ expect(() => controller!.loadFlutterAsset(''), throwsAssertionError);
+ });
+
+ testWidgets('Load HTML string without base URL', (WidgetTester tester) async {
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+
+ await controller!.loadHtmlString('<p>This is a test paragraph.</p>');
+
+ verify(mockWebViewPlatformController.loadHtmlString(
+ '<p>This is a test paragraph.</p>',
+ ));
+ });
+
+ testWidgets('Load HTML string with base URL', (WidgetTester tester) async {
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+
+ await controller!.loadHtmlString(
+ '<p>This is a test paragraph.</p>',
+ baseUrl: 'https://flutter.dev',
+ );
+
+ verify(mockWebViewPlatformController.loadHtmlString(
+ '<p>This is a test paragraph.</p>',
+ baseUrl: 'https://flutter.dev',
+ ));
+ });
+
+ testWidgets('Load HTML string with empty string',
+ (WidgetTester tester) async {
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+
+ expect(() => controller!.loadHtmlString(''), throwsAssertionError);
+ });
+
+ testWidgets('Load url', (WidgetTester tester) async {
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+
+ await controller!.loadUrl('https://flutter.io');
+
+ verify(mockWebViewPlatformController.loadUrl(
+ 'https://flutter.io',
+ argThat(isNull),
+ ));
+ });
+
+ testWidgets('Invalid urls', (WidgetTester tester) async {
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+
+ final CreationParams params = captureBuildArgs(
+ mockWebViewPlatform,
+ creationParams: true,
+ ).single as CreationParams;
+
+ expect(params.initialUrl, isNull);
+
+ expect(() => controller!.loadUrl(''), throwsA(anything));
+ expect(() => controller!.loadUrl('flutter.io'), throwsA(anything));
+ });
+
+ testWidgets('Headers in loadUrl', (WidgetTester tester) async {
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+
+ final Map<String, String> headers = <String, String>{
+ 'CACHE-CONTROL': 'ABC'
+ };
+ await controller!.loadUrl('https://flutter.io', headers: headers);
+
+ verify(mockWebViewPlatformController.loadUrl(
+ 'https://flutter.io',
+ <String, String>{'CACHE-CONTROL': 'ABC'},
+ ));
+ });
+
+ testWidgets('loadRequest', (WidgetTester tester) async {
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+ expect(controller, isNotNull);
+
+ final WebViewRequest req = WebViewRequest(
+ uri: Uri.parse('https://flutter.dev'),
+ method: WebViewRequestMethod.post,
+ headers: <String, String>{'foo': 'bar'},
+ body: Uint8List.fromList('Test Body'.codeUnits),
+ );
+
+ await controller!.loadRequest(req);
+
+ verify(mockWebViewPlatformController.loadRequest(req));
+ });
+
+ testWidgets('Clear Cache', (WidgetTester tester) async {
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+
+ await controller!.clearCache();
+
+ verify(mockWebViewPlatformController.clearCache());
+ });
+
+ testWidgets('Can go back', (WidgetTester tester) async {
+ when(mockWebViewPlatformController.canGoBack())
+ .thenAnswer((_) => Future<bool>.value(true));
+
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://flutter.io',
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+ expect(controller!.canGoBack(), completion(true));
+ });
+
+ testWidgets("Can't go forward", (WidgetTester tester) async {
+ when(mockWebViewPlatformController.canGoForward())
+ .thenAnswer((_) => Future<bool>.value(false));
+
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+ expect(controller!.canGoForward(), completion(false));
+ });
+
+ testWidgets('Go back', (WidgetTester tester) async {
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://youtube.com',
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ await controller!.goBack();
+ verify(mockWebViewPlatformController.goBack());
+ });
+
+ testWidgets('Go forward', (WidgetTester tester) async {
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://youtube.com',
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+ await controller!.goForward();
+ verify(mockWebViewPlatformController.goForward());
+ });
+
+ testWidgets('Current URL', (WidgetTester tester) async {
+ when(mockWebViewPlatformController.currentUrl())
+ .thenAnswer((_) => Future<String>.value('https://youtube.com'));
+
+ WebViewController? controller;
+ await tester.pumpWidget(
+ WebView(
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(controller, isNotNull);
+ expect(await controller!.currentUrl(), 'https://youtube.com');
+ });
+
+ testWidgets('Reload url', (WidgetTester tester) async {
+ late WebViewController controller;
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://flutter.io',
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ await controller.reload();
+ verify(mockWebViewPlatformController.reload());
+ });
+
+ testWidgets('evaluate Javascript', (WidgetTester tester) async {
+ when(mockWebViewPlatformController.evaluateJavascript('fake js string'))
+ .thenAnswer((_) => Future<String>.value('fake js string'));
+
+ late WebViewController controller;
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://flutter.io',
+ javascriptMode: JavascriptMode.unrestricted,
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ expect(
+ // ignore: deprecated_member_use_from_same_package
+ await controller.evaluateJavascript('fake js string'),
+ 'fake js string',
+ reason: 'should get the argument');
+ });
+
+ testWidgets('evaluate Javascript with JavascriptMode disabled',
+ (WidgetTester tester) async {
+ late WebViewController controller;
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://flutter.io',
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+ expect(
+ // ignore: deprecated_member_use_from_same_package
+ () => controller.evaluateJavascript('fake js string'),
+ throwsA(anything),
+ );
+ });
+
+ testWidgets('runJavaScript', (WidgetTester tester) async {
+ late WebViewController controller;
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://flutter.io',
+ javascriptMode: JavascriptMode.unrestricted,
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+ await controller.runJavascript('fake js string');
+ verify(mockWebViewPlatformController.runJavascript('fake js string'));
+ });
+
+ testWidgets('runJavaScript with JavascriptMode disabled',
+ (WidgetTester tester) async {
+ late WebViewController controller;
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://flutter.io',
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+ expect(
+ () => controller.runJavascript('fake js string'),
+ throwsA(anything),
+ );
+ });
+
+ testWidgets('runJavaScriptReturningResult', (WidgetTester tester) async {
+ when(mockWebViewPlatformController
+ .runJavascriptReturningResult('fake js string'))
+ .thenAnswer((_) => Future<String>.value('fake js string'));
+
+ late WebViewController controller;
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://flutter.io',
+ javascriptMode: JavascriptMode.unrestricted,
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+ expect(await controller.runJavascriptReturningResult('fake js string'),
+ 'fake js string',
+ reason: 'should get the argument');
+ });
+
+ testWidgets('runJavaScriptReturningResult with JavascriptMode disabled',
+ (WidgetTester tester) async {
+ late WebViewController controller;
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://flutter.io',
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+ expect(
+ () => controller.runJavascriptReturningResult('fake js string'),
+ throwsA(anything),
+ );
+ });
+
+ testWidgets('Cookies can be cleared once', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ const WebView(
+ initialUrl: 'https://flutter.io',
+ ),
+ );
+ final CookieManager cookieManager = CookieManager();
+ final bool hasCookies = await cookieManager.clearCookies();
+ expect(hasCookies, true);
+ });
+
+ testWidgets('Cookies can be set', (WidgetTester tester) async {
+ const WebViewCookie cookie =
+ WebViewCookie(name: 'foo', value: 'bar', domain: 'flutter.dev');
+
+ await tester.pumpWidget(
+ const WebView(
+ initialUrl: 'https://flutter.io',
+ ),
+ );
+ final CookieManager cookieManager = CookieManager();
+ await cookieManager.setCookie(cookie);
+ expect(mockWebViewCookieManagerPlatform.setCookieCalls,
+ <WebViewCookie>[cookie]);
+ });
+
+ testWidgets('Initial JavaScript channels', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://youtube.com',
+ javascriptChannels: <JavascriptChannel>{
+ JavascriptChannel(
+ name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
+ JavascriptChannel(
+ name: 'Alarm', onMessageReceived: (JavascriptMessage msg) {}),
+ },
+ ),
+ );
+
+ final CreationParams params = captureBuildArgs(
+ mockWebViewPlatform,
+ creationParams: true,
+ ).single as CreationParams;
+
+ expect(params.javascriptChannelNames,
+ unorderedEquals(<String>['Tts', 'Alarm']));
+ });
+
+ test('Only valid JavaScript channel names are allowed', () {
+ void noOp(JavascriptMessage msg) {}
+ JavascriptChannel(name: 'Tts1', onMessageReceived: noOp);
+ JavascriptChannel(name: '_Alarm', onMessageReceived: noOp);
+ JavascriptChannel(name: 'foo_bar_', onMessageReceived: noOp);
+
+ VoidCallback createChannel(String name) {
+ return () {
+ JavascriptChannel(name: name, onMessageReceived: noOp);
+ };
+ }
+
+ expect(createChannel('1Alarm'), throwsAssertionError);
+ expect(createChannel('foo.bar'), throwsAssertionError);
+ expect(createChannel(''), throwsAssertionError);
+ });
+
+ testWidgets('Unique JavaScript channel names are required',
+ (WidgetTester tester) async {
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://youtube.com',
+ javascriptChannels: <JavascriptChannel>{
+ JavascriptChannel(
+ name: 'Alarm', onMessageReceived: (JavascriptMessage msg) {}),
+ JavascriptChannel(
+ name: 'Alarm', onMessageReceived: (JavascriptMessage msg) {}),
+ },
+ ),
+ );
+ expect(tester.takeException(), isNot(null));
+ });
+
+ testWidgets('JavaScript channels update', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://youtube.com',
+ javascriptChannels: <JavascriptChannel>{
+ JavascriptChannel(
+ name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
+ JavascriptChannel(
+ name: 'Alarm', onMessageReceived: (JavascriptMessage msg) {}),
+ },
+ ),
+ );
+
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://youtube.com',
+ javascriptChannels: <JavascriptChannel>{
+ JavascriptChannel(
+ name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
+ JavascriptChannel(
+ name: 'Alarm2', onMessageReceived: (JavascriptMessage msg) {}),
+ JavascriptChannel(
+ name: 'Alarm3', onMessageReceived: (JavascriptMessage msg) {}),
+ },
+ ),
+ );
+
+ final JavascriptChannelRegistry channelRegistry = captureBuildArgs(
+ mockWebViewPlatform,
+ javascriptChannelRegistry: true,
+ ).first as JavascriptChannelRegistry;
+
+ expect(
+ channelRegistry.channels.keys,
+ unorderedEquals(<String>['Tts', 'Alarm2', 'Alarm3']),
+ );
+ });
+
+ testWidgets('Remove all JavaScript channels and then add',
+ (WidgetTester tester) async {
+ // This covers a specific bug we had where after updating javascriptChannels to null,
+ // updating it again with a subset of the previously registered channels fails as the
+ // widget's cache of current channel wasn't properly updated when updating javascriptChannels to
+ // null.
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://youtube.com',
+ javascriptChannels: <JavascriptChannel>{
+ JavascriptChannel(
+ name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
+ },
+ ),
+ );
+
+ await tester.pumpWidget(
+ const WebView(
+ initialUrl: 'https://youtube.com',
+ ),
+ );
+
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://youtube.com',
+ javascriptChannels: <JavascriptChannel>{
+ JavascriptChannel(
+ name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
+ },
+ ),
+ );
+
+ final JavascriptChannelRegistry channelRegistry = captureBuildArgs(
+ mockWebViewPlatform,
+ javascriptChannelRegistry: true,
+ ).last as JavascriptChannelRegistry;
+
+ expect(channelRegistry.channels.keys, unorderedEquals(<String>['Tts']));
+ });
+
+ testWidgets('JavaScript channel messages', (WidgetTester tester) async {
+ final List<String> ttsMessagesReceived = <String>[];
+ final List<String> alarmMessagesReceived = <String>[];
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://youtube.com',
+ javascriptChannels: <JavascriptChannel>{
+ JavascriptChannel(
+ name: 'Tts',
+ onMessageReceived: (JavascriptMessage msg) {
+ ttsMessagesReceived.add(msg.message);
+ }),
+ JavascriptChannel(
+ name: 'Alarm',
+ onMessageReceived: (JavascriptMessage msg) {
+ alarmMessagesReceived.add(msg.message);
+ }),
+ },
+ ),
+ );
+
+ final JavascriptChannelRegistry channelRegistry = captureBuildArgs(
+ mockWebViewPlatform,
+ javascriptChannelRegistry: true,
+ ).single as JavascriptChannelRegistry;
+
+ expect(ttsMessagesReceived, isEmpty);
+ expect(alarmMessagesReceived, isEmpty);
+
+ channelRegistry.onJavascriptChannelMessage('Tts', 'Hello');
+ channelRegistry.onJavascriptChannelMessage('Tts', 'World');
+
+ expect(ttsMessagesReceived, <String>['Hello', 'World']);
+ });
+
+ group('$PageStartedCallback', () {
+ testWidgets('onPageStarted is not null', (WidgetTester tester) async {
+ String? returnedUrl;
+
+ await tester.pumpWidget(WebView(
+ initialUrl: 'https://youtube.com',
+ onPageStarted: (String url) {
+ returnedUrl = url;
+ },
+ ));
+
+ final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
+ mockWebViewPlatform,
+ webViewPlatformCallbacksHandler: true,
+ ).single as WebViewPlatformCallbacksHandler;
+
+ handler.onPageStarted('https://youtube.com');
+
+ expect(returnedUrl, 'https://youtube.com');
+ });
+
+ testWidgets('onPageStarted is null', (WidgetTester tester) async {
+ await tester.pumpWidget(const WebView(
+ initialUrl: 'https://youtube.com',
+ ));
+
+ final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
+ mockWebViewPlatform,
+ webViewPlatformCallbacksHandler: true,
+ ).single as WebViewPlatformCallbacksHandler;
+
+ // The platform side will always invoke a call for onPageStarted. This is
+ // to test that it does not crash on a null callback.
+ handler.onPageStarted('https://youtube.com');
+ });
+
+ testWidgets('onPageStarted changed', (WidgetTester tester) async {
+ String? returnedUrl;
+
+ await tester.pumpWidget(WebView(
+ initialUrl: 'https://youtube.com',
+ onPageStarted: (String url) {},
+ ));
+
+ await tester.pumpWidget(WebView(
+ initialUrl: 'https://youtube.com',
+ onPageStarted: (String url) {
+ returnedUrl = url;
+ },
+ ));
+
+ final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
+ mockWebViewPlatform,
+ webViewPlatformCallbacksHandler: true,
+ ).last as WebViewPlatformCallbacksHandler;
+ handler.onPageStarted('https://youtube.com');
+
+ expect(returnedUrl, 'https://youtube.com');
+ });
+ });
+
+ group('$PageFinishedCallback', () {
+ testWidgets('onPageFinished is not null', (WidgetTester tester) async {
+ String? returnedUrl;
+
+ await tester.pumpWidget(WebView(
+ initialUrl: 'https://youtube.com',
+ onPageFinished: (String url) {
+ returnedUrl = url;
+ },
+ ));
+
+ final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
+ mockWebViewPlatform,
+ webViewPlatformCallbacksHandler: true,
+ ).single as WebViewPlatformCallbacksHandler;
+ handler.onPageFinished('https://youtube.com');
+
+ expect(returnedUrl, 'https://youtube.com');
+ });
+
+ testWidgets('onPageFinished is null', (WidgetTester tester) async {
+ await tester.pumpWidget(const WebView(
+ initialUrl: 'https://youtube.com',
+ ));
+
+ final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
+ mockWebViewPlatform,
+ webViewPlatformCallbacksHandler: true,
+ ).single as WebViewPlatformCallbacksHandler;
+ // The platform side will always invoke a call for onPageFinished. This is
+ // to test that it does not crash on a null callback.
+ handler.onPageFinished('https://youtube.com');
+ });
+
+ testWidgets('onPageFinished changed', (WidgetTester tester) async {
+ String? returnedUrl;
+
+ await tester.pumpWidget(WebView(
+ initialUrl: 'https://youtube.com',
+ onPageFinished: (String url) {},
+ ));
+
+ await tester.pumpWidget(WebView(
+ initialUrl: 'https://youtube.com',
+ onPageFinished: (String url) {
+ returnedUrl = url;
+ },
+ ));
+
+ final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
+ mockWebViewPlatform,
+ webViewPlatformCallbacksHandler: true,
+ ).last as WebViewPlatformCallbacksHandler;
+ handler.onPageFinished('https://youtube.com');
+
+ expect(returnedUrl, 'https://youtube.com');
+ });
+ });
+
+ group('$PageLoadingCallback', () {
+ testWidgets('onLoadingProgress is not null', (WidgetTester tester) async {
+ int? loadingProgress;
+
+ await tester.pumpWidget(WebView(
+ initialUrl: 'https://youtube.com',
+ onProgress: (int progress) {
+ loadingProgress = progress;
+ },
+ ));
+
+ final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
+ mockWebViewPlatform,
+ webViewPlatformCallbacksHandler: true,
+ ).single as WebViewPlatformCallbacksHandler;
+ handler.onProgress(50);
+
+ expect(loadingProgress, 50);
+ });
+
+ testWidgets('onLoadingProgress is null', (WidgetTester tester) async {
+ await tester.pumpWidget(const WebView(
+ initialUrl: 'https://youtube.com',
+ ));
+
+ final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
+ mockWebViewPlatform,
+ webViewPlatformCallbacksHandler: true,
+ ).single as WebViewPlatformCallbacksHandler;
+
+ // This is to test that it does not crash on a null callback.
+ handler.onProgress(50);
+ });
+
+ testWidgets('onLoadingProgress changed', (WidgetTester tester) async {
+ int? loadingProgress;
+
+ await tester.pumpWidget(WebView(
+ initialUrl: 'https://youtube.com',
+ onProgress: (int progress) {},
+ ));
+
+ await tester.pumpWidget(WebView(
+ initialUrl: 'https://youtube.com',
+ onProgress: (int progress) {
+ loadingProgress = progress;
+ },
+ ));
+
+ final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
+ mockWebViewPlatform,
+ webViewPlatformCallbacksHandler: true,
+ ).last as WebViewPlatformCallbacksHandler;
+ handler.onProgress(50);
+
+ expect(loadingProgress, 50);
+ });
+ });
+
+ group('navigationDelegate', () {
+ testWidgets('hasNavigationDelegate', (WidgetTester tester) async {
+ await tester.pumpWidget(const WebView(
+ initialUrl: 'https://youtube.com',
+ ));
+
+ final CreationParams params = captureBuildArgs(
+ mockWebViewPlatform,
+ creationParams: true,
+ ).single as CreationParams;
+
+ expect(params.webSettings!.hasNavigationDelegate, false);
+
+ await tester.pumpWidget(WebView(
+ initialUrl: 'https://youtube.com',
+ navigationDelegate: (NavigationRequest r) =>
+ NavigationDecision.navigate,
+ ));
+
+ final WebSettings updateSettings =
+ verify(mockWebViewPlatformController.updateSettings(captureAny))
+ .captured
+ .single as WebSettings;
+
+ expect(updateSettings.hasNavigationDelegate, true);
+ });
+
+ testWidgets('Block navigation', (WidgetTester tester) async {
+ final List<NavigationRequest> navigationRequests = <NavigationRequest>[];
+
+ await tester.pumpWidget(WebView(
+ initialUrl: 'https://youtube.com',
+ navigationDelegate: (NavigationRequest request) {
+ navigationRequests.add(request);
+ // Only allow navigating to https://flutter.dev
+ return request.url == 'https://flutter.dev'
+ ? NavigationDecision.navigate
+ : NavigationDecision.prevent;
+ }));
+
+ final List<dynamic> args = captureBuildArgs(
+ mockWebViewPlatform,
+ creationParams: true,
+ webViewPlatformCallbacksHandler: true,
+ );
+
+ final CreationParams params = args[0] as CreationParams;
+ expect(params.webSettings!.hasNavigationDelegate, true);
+
+ final WebViewPlatformCallbacksHandler handler =
+ args[1] as WebViewPlatformCallbacksHandler;
+
+ // The navigation delegate only allows navigation to https://flutter.dev
+ // so we should still be in https://youtube.com.
+ expect(
+ handler.onNavigationRequest(
+ url: 'https://www.google.com',
+ isForMainFrame: true,
+ ),
+ completion(false),
+ );
+
+ expect(navigationRequests.length, 1);
+ expect(navigationRequests[0].url, 'https://www.google.com');
+ expect(navigationRequests[0].isForMainFrame, true);
+
+ expect(
+ handler.onNavigationRequest(
+ url: 'https://flutter.dev',
+ isForMainFrame: true,
+ ),
+ completion(true),
+ );
+ });
+ });
+
+ group('debuggingEnabled', () {
+ testWidgets('enable debugging', (WidgetTester tester) async {
+ await tester.pumpWidget(const WebView(
+ debuggingEnabled: true,
+ ));
+
+ final CreationParams params = captureBuildArgs(
+ mockWebViewPlatform,
+ creationParams: true,
+ ).single as CreationParams;
+
+ expect(params.webSettings!.debuggingEnabled, true);
+ });
+
+ testWidgets('defaults to false', (WidgetTester tester) async {
+ await tester.pumpWidget(const WebView());
+
+ final CreationParams params = captureBuildArgs(
+ mockWebViewPlatform,
+ creationParams: true,
+ ).single as CreationParams;
+
+ expect(params.webSettings!.debuggingEnabled, false);
+ });
+
+ testWidgets('can be changed', (WidgetTester tester) async {
+ final GlobalKey key = GlobalKey();
+ await tester.pumpWidget(WebView(key: key));
+
+ await tester.pumpWidget(WebView(
+ key: key,
+ debuggingEnabled: true,
+ ));
+
+ final WebSettings enabledSettings =
+ verify(mockWebViewPlatformController.updateSettings(captureAny))
+ .captured
+ .last as WebSettings;
+ expect(enabledSettings.debuggingEnabled, true);
+
+ await tester.pumpWidget(WebView(
+ key: key,
+ ));
+
+ final WebSettings disabledSettings =
+ verify(mockWebViewPlatformController.updateSettings(captureAny))
+ .captured
+ .last as WebSettings;
+ expect(disabledSettings.debuggingEnabled, false);
+ });
+ });
+
+ group('zoomEnabled', () {
+ testWidgets('Enable zoom', (WidgetTester tester) async {
+ await tester.pumpWidget(const WebView());
+
+ final CreationParams params = captureBuildArgs(
+ mockWebViewPlatform,
+ creationParams: true,
+ ).single as CreationParams;
+
+ expect(params.webSettings!.zoomEnabled, isTrue);
+ });
+
+ testWidgets('defaults to true', (WidgetTester tester) async {
+ await tester.pumpWidget(const WebView());
+
+ final CreationParams params = captureBuildArgs(
+ mockWebViewPlatform,
+ creationParams: true,
+ ).single as CreationParams;
+
+ expect(params.webSettings!.zoomEnabled, isTrue);
+ });
+
+ testWidgets('can be changed', (WidgetTester tester) async {
+ final GlobalKey key = GlobalKey();
+ await tester.pumpWidget(WebView(key: key));
+
+ await tester.pumpWidget(WebView(
+ key: key,
+ ));
+
+ final WebSettings enabledSettings =
+ verify(mockWebViewPlatformController.updateSettings(captureAny))
+ .captured
+ .last as WebSettings;
+ // Zoom defaults to true, so no changes are made to settings.
+ expect(enabledSettings.zoomEnabled, isNull);
+
+ await tester.pumpWidget(WebView(
+ key: key,
+ zoomEnabled: false,
+ ));
+
+ final WebSettings disabledSettings =
+ verify(mockWebViewPlatformController.updateSettings(captureAny))
+ .captured
+ .last as WebSettings;
+ expect(disabledSettings.zoomEnabled, isFalse);
+ });
+ });
+
+ group('Background color', () {
+ testWidgets('Defaults to null', (WidgetTester tester) async {
+ await tester.pumpWidget(const WebView());
+
+ final CreationParams params = captureBuildArgs(
+ mockWebViewPlatform,
+ creationParams: true,
+ ).single as CreationParams;
+
+ expect(params.backgroundColor, null);
+ });
+
+ testWidgets('Can be transparent', (WidgetTester tester) async {
+ const Color transparentColor = Color(0x00000000);
+
+ await tester.pumpWidget(const WebView(
+ backgroundColor: transparentColor,
+ ));
+
+ final CreationParams params = captureBuildArgs(
+ mockWebViewPlatform,
+ creationParams: true,
+ ).single as CreationParams;
+
+ expect(params.backgroundColor, transparentColor);
+ });
+ });
+
+ group('Custom platform implementation', () {
+ setUp(() {
+ WebView.platform = MyWebViewPlatform();
+ });
+ tearDownAll(() {
+ WebView.platform = null;
+ });
+
+ testWidgets('creation', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ const WebView(
+ initialUrl: 'https://youtube.com',
+ gestureNavigationEnabled: true,
+ ),
+ );
+
+ final MyWebViewPlatform builder = WebView.platform as MyWebViewPlatform;
+ final MyWebViewPlatformController platform = builder.lastPlatformBuilt!;
+
+ expect(
+ platform.creationParams,
+ MatchesCreationParams(CreationParams(
+ initialUrl: 'https://youtube.com',
+ webSettings: WebSettings(
+ javascriptMode: JavascriptMode.disabled,
+ hasNavigationDelegate: false,
+ debuggingEnabled: false,
+ userAgent: const WebSetting<String?>.of(null),
+ gestureNavigationEnabled: true,
+ zoomEnabled: true,
+ ),
+ )));
+ });
+
+ testWidgets('loadUrl', (WidgetTester tester) async {
+ late WebViewController controller;
+ await tester.pumpWidget(
+ WebView(
+ initialUrl: 'https://youtube.com',
+ onWebViewCreated: (WebViewController webViewController) {
+ controller = webViewController;
+ },
+ ),
+ );
+
+ final MyWebViewPlatform builder = WebView.platform as MyWebViewPlatform;
+ final MyWebViewPlatformController platform = builder.lastPlatformBuilt!;
+
+ final Map<String, String> headers = <String, String>{
+ 'header': 'value',
+ };
+
+ await controller.loadUrl('https://google.com', headers: headers);
+
+ expect(platform.lastUrlLoaded, 'https://google.com');
+ expect(platform.lastRequestHeaders, headers);
+ });
+ });
+
+ testWidgets('Set UserAgent', (WidgetTester tester) async {
+ await tester.pumpWidget(const WebView(
+ initialUrl: 'https://youtube.com',
+ javascriptMode: JavascriptMode.unrestricted,
+ ));
+
+ final CreationParams params = captureBuildArgs(
+ mockWebViewPlatform,
+ creationParams: true,
+ ).single as CreationParams;
+
+ expect(params.webSettings!.userAgent.value, isNull);
+
+ await tester.pumpWidget(const WebView(
+ initialUrl: 'https://youtube.com',
+ javascriptMode: JavascriptMode.unrestricted,
+ userAgent: 'UA',
+ ));
+
+ final WebSettings settings =
+ verify(mockWebViewPlatformController.updateSettings(captureAny))
+ .captured
+ .last as WebSettings;
+ expect(settings.userAgent.value, 'UA');
+ });
+}
+
+List<dynamic> captureBuildArgs(
+ MockWebViewPlatform mockWebViewPlatform, {
+ bool context = false,
+ bool creationParams = false,
+ bool webViewPlatformCallbacksHandler = false,
+ bool javascriptChannelRegistry = false,
+ bool onWebViewPlatformCreated = false,
+ bool gestureRecognizers = false,
+}) {
+ return verify(mockWebViewPlatform.build(
+ context: context ? captureAnyNamed('context') : anyNamed('context'),
+ creationParams: creationParams
+ ? captureAnyNamed('creationParams')
+ : anyNamed('creationParams'),
+ webViewPlatformCallbacksHandler: webViewPlatformCallbacksHandler
+ ? captureAnyNamed('webViewPlatformCallbacksHandler')
+ : anyNamed('webViewPlatformCallbacksHandler'),
+ javascriptChannelRegistry: javascriptChannelRegistry
+ ? captureAnyNamed('javascriptChannelRegistry')
+ : anyNamed('javascriptChannelRegistry'),
+ onWebViewPlatformCreated: onWebViewPlatformCreated
+ ? captureAnyNamed('onWebViewPlatformCreated')
+ : anyNamed('onWebViewPlatformCreated'),
+ gestureRecognizers: gestureRecognizers
+ ? captureAnyNamed('gestureRecognizers')
+ : anyNamed('gestureRecognizers'),
+ )).captured;
+}
+
+// This Widget ensures that onWebViewPlatformCreated is only called once when
+// making multiple calls to `WidgetTester.pumpWidget` with different parameters
+// for the WebView.
+class TestPlatformWebView extends StatefulWidget {
+ const TestPlatformWebView({
+ super.key,
+ required this.mockWebViewPlatformController,
+ this.onWebViewPlatformCreated,
+ });
+
+ final MockWebViewPlatformController mockWebViewPlatformController;
+ final WebViewPlatformCreatedCallback? onWebViewPlatformCreated;
+
+ @override
+ State<StatefulWidget> createState() => TestPlatformWebViewState();
+}
+
+class TestPlatformWebViewState extends State<TestPlatformWebView> {
+ @override
+ void initState() {
+ super.initState();
+ final WebViewPlatformCreatedCallback? onWebViewPlatformCreated =
+ widget.onWebViewPlatformCreated;
+ if (onWebViewPlatformCreated != null) {
+ onWebViewPlatformCreated(widget.mockWebViewPlatformController);
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}
+
+class MyWebViewPlatform implements WebViewPlatform {
+ MyWebViewPlatformController? lastPlatformBuilt;
+
+ @override
+ Widget build({
+ BuildContext? context,
+ CreationParams? creationParams,
+ required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
+ required JavascriptChannelRegistry javascriptChannelRegistry,
+ WebViewPlatformCreatedCallback? onWebViewPlatformCreated,
+ Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
+ }) {
+ assert(onWebViewPlatformCreated != null);
+ lastPlatformBuilt = MyWebViewPlatformController(
+ creationParams, gestureRecognizers, webViewPlatformCallbacksHandler);
+ onWebViewPlatformCreated!(lastPlatformBuilt);
+ return Container();
+ }
+
+ @override
+ Future<bool> clearCookies() {
+ return Future<bool>.sync(() => true);
+ }
+}
+
+class MyWebViewPlatformController extends WebViewPlatformController {
+ MyWebViewPlatformController(this.creationParams, this.gestureRecognizers,
+ WebViewPlatformCallbacksHandler platformHandler)
+ : super(platformHandler);
+
+ CreationParams? creationParams;
+ Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers;
+
+ String? lastUrlLoaded;
+ Map<String, String>? lastRequestHeaders;
+
+ @override
+ Future<void> loadUrl(String url, Map<String, String>? headers) async {
+ equals(1, 1);
+ lastUrlLoaded = url;
+ lastRequestHeaders = headers;
+ }
+}
+
+class MatchesWebSettings extends Matcher {
+ MatchesWebSettings(this._webSettings);
+
+ final WebSettings? _webSettings;
+
+ @override
+ Description describe(Description description) =>
+ description.add('$_webSettings');
+
+ @override
+ bool matches(
+ covariant WebSettings webSettings, Map<dynamic, dynamic> matchState) {
+ return _webSettings!.javascriptMode == webSettings.javascriptMode &&
+ _webSettings!.hasNavigationDelegate ==
+ webSettings.hasNavigationDelegate &&
+ _webSettings!.debuggingEnabled == webSettings.debuggingEnabled &&
+ _webSettings!.gestureNavigationEnabled ==
+ webSettings.gestureNavigationEnabled &&
+ _webSettings!.userAgent == webSettings.userAgent &&
+ _webSettings!.zoomEnabled == webSettings.zoomEnabled;
+ }
+}
+
+class MatchesCreationParams extends Matcher {
+ MatchesCreationParams(this._creationParams);
+
+ final CreationParams _creationParams;
+
+ @override
+ Description describe(Description description) =>
+ description.add('$_creationParams');
+
+ @override
+ bool matches(covariant CreationParams creationParams,
+ Map<dynamic, dynamic> matchState) {
+ return _creationParams.initialUrl == creationParams.initialUrl &&
+ MatchesWebSettings(_creationParams.webSettings)
+ .matches(creationParams.webSettings!, matchState) &&
+ orderedEquals(_creationParams.javascriptChannelNames)
+ .matches(creationParams.javascriptChannelNames, matchState);
+ }
+}
+
+class MockWebViewCookieManagerPlatform extends WebViewCookieManagerPlatform {
+ List<WebViewCookie> setCookieCalls = <WebViewCookie>[];
+
+ @override
+ Future<bool> clearCookies() async => true;
+
+ @override
+ Future<void> setCookie(WebViewCookie cookie) async {
+ setCookieCalls.add(cookie);
+ }
+
+ void reset() {
+ setCookieCalls = <WebViewCookie>[];
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter/test/legacy/webview_flutter_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/legacy/webview_flutter_test.mocks.dart
new file mode 100644
index 0000000..a40cf34
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter/test/legacy/webview_flutter_test.mocks.dart
@@ -0,0 +1,346 @@
+// Mocks generated by Mockito 5.3.2 from annotations
+// in webview_flutter/test/legacy/webview_flutter_test.dart.
+// Do not manually edit this file.
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:async' as _i9;
+
+import 'package:flutter/foundation.dart' as _i3;
+import 'package:flutter/gestures.dart' as _i8;
+import 'package:flutter/widgets.dart' as _i2;
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:webview_flutter_platform_interface/src/legacy/platform_interface/javascript_channel_registry.dart'
+ as _i7;
+import 'package:webview_flutter_platform_interface/src/legacy/platform_interface/webview_platform.dart'
+ as _i4;
+import 'package:webview_flutter_platform_interface/src/legacy/platform_interface/webview_platform_callbacks_handler.dart'
+ as _i6;
+import 'package:webview_flutter_platform_interface/src/legacy/platform_interface/webview_platform_controller.dart'
+ as _i10;
+import 'package:webview_flutter_platform_interface/src/legacy/types/types.dart'
+ as _i5;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+class _FakeWidget_0 extends _i1.SmartFake implements _i2.Widget {
+ _FakeWidget_0(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+
+ @override
+ String toString({_i3.DiagnosticLevel? minLevel = _i3.DiagnosticLevel.info}) =>
+ super.toString();
+}
+
+/// A class which mocks [WebViewPlatform].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebViewPlatform extends _i1.Mock implements _i4.WebViewPlatform {
+ MockWebViewPlatform() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i2.Widget build({
+ required _i2.BuildContext? context,
+ required _i5.CreationParams? creationParams,
+ required _i6.WebViewPlatformCallbacksHandler?
+ webViewPlatformCallbacksHandler,
+ required _i7.JavascriptChannelRegistry? javascriptChannelRegistry,
+ _i4.WebViewPlatformCreatedCallback? onWebViewPlatformCreated,
+ Set<_i3.Factory<_i8.OneSequenceGestureRecognizer>>? gestureRecognizers,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #build,
+ [],
+ {
+ #context: context,
+ #creationParams: creationParams,
+ #webViewPlatformCallbacksHandler: webViewPlatformCallbacksHandler,
+ #javascriptChannelRegistry: javascriptChannelRegistry,
+ #onWebViewPlatformCreated: onWebViewPlatformCreated,
+ #gestureRecognizers: gestureRecognizers,
+ },
+ ),
+ returnValue: _FakeWidget_0(
+ this,
+ Invocation.method(
+ #build,
+ [],
+ {
+ #context: context,
+ #creationParams: creationParams,
+ #webViewPlatformCallbacksHandler: webViewPlatformCallbacksHandler,
+ #javascriptChannelRegistry: javascriptChannelRegistry,
+ #onWebViewPlatformCreated: onWebViewPlatformCreated,
+ #gestureRecognizers: gestureRecognizers,
+ },
+ ),
+ ),
+ ) as _i2.Widget);
+ @override
+ _i9.Future<bool> clearCookies() => (super.noSuchMethod(
+ Invocation.method(
+ #clearCookies,
+ [],
+ ),
+ returnValue: _i9.Future<bool>.value(false),
+ ) as _i9.Future<bool>);
+}
+
+/// A class which mocks [WebViewPlatformController].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebViewPlatformController extends _i1.Mock
+ implements _i10.WebViewPlatformController {
+ MockWebViewPlatformController() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i9.Future<void> loadFile(String? absoluteFilePath) => (super.noSuchMethod(
+ Invocation.method(
+ #loadFile,
+ [absoluteFilePath],
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<void> loadFlutterAsset(String? key) => (super.noSuchMethod(
+ Invocation.method(
+ #loadFlutterAsset,
+ [key],
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<void> loadHtmlString(
+ String? html, {
+ String? baseUrl,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #loadHtmlString,
+ [html],
+ {#baseUrl: baseUrl},
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<void> loadUrl(
+ String? url,
+ Map<String, String>? headers,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #loadUrl,
+ [
+ url,
+ headers,
+ ],
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<void> loadRequest(_i5.WebViewRequest? request) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #loadRequest,
+ [request],
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<void> updateSettings(_i5.WebSettings? setting) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #updateSettings,
+ [setting],
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<String?> currentUrl() => (super.noSuchMethod(
+ Invocation.method(
+ #currentUrl,
+ [],
+ ),
+ returnValue: _i9.Future<String?>.value(),
+ ) as _i9.Future<String?>);
+ @override
+ _i9.Future<bool> canGoBack() => (super.noSuchMethod(
+ Invocation.method(
+ #canGoBack,
+ [],
+ ),
+ returnValue: _i9.Future<bool>.value(false),
+ ) as _i9.Future<bool>);
+ @override
+ _i9.Future<bool> canGoForward() => (super.noSuchMethod(
+ Invocation.method(
+ #canGoForward,
+ [],
+ ),
+ returnValue: _i9.Future<bool>.value(false),
+ ) as _i9.Future<bool>);
+ @override
+ _i9.Future<void> goBack() => (super.noSuchMethod(
+ Invocation.method(
+ #goBack,
+ [],
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<void> goForward() => (super.noSuchMethod(
+ Invocation.method(
+ #goForward,
+ [],
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<void> reload() => (super.noSuchMethod(
+ Invocation.method(
+ #reload,
+ [],
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<void> clearCache() => (super.noSuchMethod(
+ Invocation.method(
+ #clearCache,
+ [],
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<String> evaluateJavascript(String? javascript) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #evaluateJavascript,
+ [javascript],
+ ),
+ returnValue: _i9.Future<String>.value(''),
+ ) as _i9.Future<String>);
+ @override
+ _i9.Future<void> runJavascript(String? javascript) => (super.noSuchMethod(
+ Invocation.method(
+ #runJavascript,
+ [javascript],
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<String> runJavascriptReturningResult(String? javascript) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #runJavascriptReturningResult,
+ [javascript],
+ ),
+ returnValue: _i9.Future<String>.value(''),
+ ) as _i9.Future<String>);
+ @override
+ _i9.Future<void> addJavascriptChannels(Set<String>? javascriptChannelNames) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #addJavascriptChannels,
+ [javascriptChannelNames],
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<void> removeJavascriptChannels(
+ Set<String>? javascriptChannelNames) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #removeJavascriptChannels,
+ [javascriptChannelNames],
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<String?> getTitle() => (super.noSuchMethod(
+ Invocation.method(
+ #getTitle,
+ [],
+ ),
+ returnValue: _i9.Future<String?>.value(),
+ ) as _i9.Future<String?>);
+ @override
+ _i9.Future<void> scrollTo(
+ int? x,
+ int? y,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #scrollTo,
+ [
+ x,
+ y,
+ ],
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<void> scrollBy(
+ int? x,
+ int? y,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #scrollBy,
+ [
+ x,
+ y,
+ ],
+ ),
+ returnValue: _i9.Future<void>.value(),
+ returnValueForMissingStub: _i9.Future<void>.value(),
+ ) as _i9.Future<void>);
+ @override
+ _i9.Future<int> getScrollX() => (super.noSuchMethod(
+ Invocation.method(
+ #getScrollX,
+ [],
+ ),
+ returnValue: _i9.Future<int>.value(0),
+ ) as _i9.Future<int>);
+ @override
+ _i9.Future<int> getScrollY() => (super.noSuchMethod(
+ Invocation.method(
+ #getScrollY,
+ [],
+ ),
+ returnValue: _i9.Future<int>.value(0),
+ ) as _i9.Future<int>);
+}
diff --git a/packages/webview_flutter/webview_flutter/test/navigation_delegate_test.dart b/packages/webview_flutter/webview_flutter/test/navigation_delegate_test.dart
new file mode 100644
index 0000000..839454e
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter/test/navigation_delegate_test.dart
@@ -0,0 +1,91 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+import 'package:plugin_platform_interface/plugin_platform_interface.dart';
+import 'package:webview_flutter/webview_flutter.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+
+import 'navigation_delegate_test.mocks.dart';
+
+@GenerateMocks(<Type>[WebViewPlatform, PlatformNavigationDelegate])
+void main() {
+ group('NavigationDelegate', () {
+ test('onNavigationRequest', () async {
+ WebViewPlatform.instance = TestWebViewPlatform();
+
+ NavigationDecision onNavigationRequest(NavigationRequest request) {
+ return NavigationDecision.navigate;
+ }
+
+ final NavigationDelegate delegate = NavigationDelegate(
+ onNavigationRequest: onNavigationRequest,
+ );
+
+ verify(delegate.platform.setOnNavigationRequest(onNavigationRequest));
+ });
+
+ test('onPageStarted', () async {
+ WebViewPlatform.instance = TestWebViewPlatform();
+
+ void onPageStarted(String url) {}
+
+ final NavigationDelegate delegate = NavigationDelegate(
+ onPageStarted: onPageStarted,
+ );
+
+ verify(delegate.platform.setOnPageStarted(onPageStarted));
+ });
+
+ test('onPageFinished', () async {
+ WebViewPlatform.instance = TestWebViewPlatform();
+
+ void onPageFinished(String url) {}
+
+ final NavigationDelegate delegate = NavigationDelegate(
+ onPageFinished: onPageFinished,
+ );
+
+ verify(delegate.platform.setOnPageFinished(onPageFinished));
+ });
+
+ test('onProgress', () async {
+ WebViewPlatform.instance = TestWebViewPlatform();
+
+ void onProgress(int progress) {}
+
+ final NavigationDelegate delegate = NavigationDelegate(
+ onProgress: onProgress,
+ );
+
+ verify(delegate.platform.setOnProgress(onProgress));
+ });
+
+ test('onWebResourceError', () async {
+ WebViewPlatform.instance = TestWebViewPlatform();
+
+ void onWebResourceError(WebResourceError error) {}
+
+ final NavigationDelegate delegate = NavigationDelegate(
+ onWebResourceError: onWebResourceError,
+ );
+
+ verify(delegate.platform.setOnWebResourceError(onWebResourceError));
+ });
+ });
+}
+
+class TestWebViewPlatform extends WebViewPlatform {
+ @override
+ PlatformNavigationDelegate createPlatformNavigationDelegate(
+ PlatformNavigationDelegateCreationParams params,
+ ) {
+ return TestMockPlatformNavigationDelegate();
+ }
+}
+
+class TestMockPlatformNavigationDelegate extends MockPlatformNavigationDelegate
+ with MockPlatformInterfaceMixin {}
diff --git a/packages/webview_flutter/webview_flutter/test/navigation_delegate_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/navigation_delegate_test.mocks.dart
new file mode 100644
index 0000000..a7ac41e
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter/test/navigation_delegate_test.mocks.dart
@@ -0,0 +1,231 @@
+// Mocks generated by Mockito 5.3.2 from annotations
+// in webview_flutter/test/navigation_delegate_test.dart.
+// Do not manually edit this file.
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:async' as _i8;
+
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:webview_flutter_platform_interface/src/platform_navigation_delegate.dart'
+ as _i3;
+import 'package:webview_flutter_platform_interface/src/platform_webview_controller.dart'
+ as _i4;
+import 'package:webview_flutter_platform_interface/src/platform_webview_cookie_manager.dart'
+ as _i2;
+import 'package:webview_flutter_platform_interface/src/platform_webview_widget.dart'
+ as _i5;
+import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i6;
+import 'package:webview_flutter_platform_interface/src/webview_platform.dart'
+ as _i7;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+class _FakePlatformWebViewCookieManager_0 extends _i1.SmartFake
+ implements _i2.PlatformWebViewCookieManager {
+ _FakePlatformWebViewCookieManager_0(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakePlatformNavigationDelegate_1 extends _i1.SmartFake
+ implements _i3.PlatformNavigationDelegate {
+ _FakePlatformNavigationDelegate_1(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakePlatformWebViewController_2 extends _i1.SmartFake
+ implements _i4.PlatformWebViewController {
+ _FakePlatformWebViewController_2(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakePlatformWebViewWidget_3 extends _i1.SmartFake
+ implements _i5.PlatformWebViewWidget {
+ _FakePlatformWebViewWidget_3(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakePlatformNavigationDelegateCreationParams_4 extends _i1.SmartFake
+ implements _i6.PlatformNavigationDelegateCreationParams {
+ _FakePlatformNavigationDelegateCreationParams_4(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+/// A class which mocks [WebViewPlatform].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebViewPlatform extends _i1.Mock implements _i7.WebViewPlatform {
+ MockWebViewPlatform() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i2.PlatformWebViewCookieManager createPlatformCookieManager(
+ _i6.PlatformWebViewCookieManagerCreationParams? params) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #createPlatformCookieManager,
+ [params],
+ ),
+ returnValue: _FakePlatformWebViewCookieManager_0(
+ this,
+ Invocation.method(
+ #createPlatformCookieManager,
+ [params],
+ ),
+ ),
+ ) as _i2.PlatformWebViewCookieManager);
+ @override
+ _i3.PlatformNavigationDelegate createPlatformNavigationDelegate(
+ _i6.PlatformNavigationDelegateCreationParams? params) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #createPlatformNavigationDelegate,
+ [params],
+ ),
+ returnValue: _FakePlatformNavigationDelegate_1(
+ this,
+ Invocation.method(
+ #createPlatformNavigationDelegate,
+ [params],
+ ),
+ ),
+ ) as _i3.PlatformNavigationDelegate);
+ @override
+ _i4.PlatformWebViewController createPlatformWebViewController(
+ _i6.PlatformWebViewControllerCreationParams? params) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #createPlatformWebViewController,
+ [params],
+ ),
+ returnValue: _FakePlatformWebViewController_2(
+ this,
+ Invocation.method(
+ #createPlatformWebViewController,
+ [params],
+ ),
+ ),
+ ) as _i4.PlatformWebViewController);
+ @override
+ _i5.PlatformWebViewWidget createPlatformWebViewWidget(
+ _i6.PlatformWebViewWidgetCreationParams? params) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #createPlatformWebViewWidget,
+ [params],
+ ),
+ returnValue: _FakePlatformWebViewWidget_3(
+ this,
+ Invocation.method(
+ #createPlatformWebViewWidget,
+ [params],
+ ),
+ ),
+ ) as _i5.PlatformWebViewWidget);
+}
+
+/// A class which mocks [PlatformNavigationDelegate].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockPlatformNavigationDelegate extends _i1.Mock
+ implements _i3.PlatformNavigationDelegate {
+ MockPlatformNavigationDelegate() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i6.PlatformNavigationDelegateCreationParams get params =>
+ (super.noSuchMethod(
+ Invocation.getter(#params),
+ returnValue: _FakePlatformNavigationDelegateCreationParams_4(
+ this,
+ Invocation.getter(#params),
+ ),
+ ) as _i6.PlatformNavigationDelegateCreationParams);
+ @override
+ _i8.Future<void> setOnNavigationRequest(
+ _i3.NavigationRequestCallback? onNavigationRequest) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setOnNavigationRequest,
+ [onNavigationRequest],
+ ),
+ returnValue: _i8.Future<void>.value(),
+ returnValueForMissingStub: _i8.Future<void>.value(),
+ ) as _i8.Future<void>);
+ @override
+ _i8.Future<void> setOnPageStarted(_i3.PageEventCallback? onPageStarted) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setOnPageStarted,
+ [onPageStarted],
+ ),
+ returnValue: _i8.Future<void>.value(),
+ returnValueForMissingStub: _i8.Future<void>.value(),
+ ) as _i8.Future<void>);
+ @override
+ _i8.Future<void> setOnPageFinished(_i3.PageEventCallback? onPageFinished) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setOnPageFinished,
+ [onPageFinished],
+ ),
+ returnValue: _i8.Future<void>.value(),
+ returnValueForMissingStub: _i8.Future<void>.value(),
+ ) as _i8.Future<void>);
+ @override
+ _i8.Future<void> setOnProgress(_i3.ProgressCallback? onProgress) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setOnProgress,
+ [onProgress],
+ ),
+ returnValue: _i8.Future<void>.value(),
+ returnValueForMissingStub: _i8.Future<void>.value(),
+ ) as _i8.Future<void>);
+ @override
+ _i8.Future<void> setOnWebResourceError(
+ _i3.WebResourceErrorCallback? onWebResourceError) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setOnWebResourceError,
+ [onWebResourceError],
+ ),
+ returnValue: _i8.Future<void>.value(),
+ returnValueForMissingStub: _i8.Future<void>.value(),
+ ) as _i8.Future<void>);
+}
diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/v4/webview_controller_test.mocks.dart
deleted file mode 100644
index f0fb4b4..0000000
--- a/packages/webview_flutter/webview_flutter/test/v4/webview_controller_test.mocks.dart
+++ /dev/null
@@ -1,203 +0,0 @@
-// Mocks generated by Mockito 5.3.0 from annotations
-// in webview_flutter/test/v4/webview_controller_test.dart.
-// Do not manually edit this file.
-
-// ignore_for_file: no_leading_underscores_for_library_prefixes
-import 'dart:async' as _i5;
-import 'dart:math' as _i3;
-import 'dart:ui' as _i7;
-
-import 'package:mockito/mockito.dart' as _i1;
-import 'package:webview_flutter_platform_interface/v4/src/platform_navigation_delegate.dart'
- as _i6;
-import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart'
- as _i4;
-import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart'
- as _i2;
-
-// ignore_for_file: type=lint
-// ignore_for_file: avoid_redundant_argument_values
-// ignore_for_file: avoid_setters_without_getters
-// ignore_for_file: comment_references
-// ignore_for_file: implementation_imports
-// ignore_for_file: invalid_use_of_visible_for_testing_member
-// ignore_for_file: prefer_const_constructors
-// ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: camel_case_types
-// ignore_for_file: subtype_of_sealed_class
-
-class _FakePlatformWebViewControllerCreationParams_0 extends _i1.SmartFake
- implements _i2.PlatformWebViewControllerCreationParams {
- _FakePlatformWebViewControllerCreationParams_0(
- Object parent, Invocation parentInvocation)
- : super(parent, parentInvocation);
-}
-
-class _FakePoint_1<T extends num> extends _i1.SmartFake
- implements _i3.Point<T> {
- _FakePoint_1(Object parent, Invocation parentInvocation)
- : super(parent, parentInvocation);
-}
-
-/// A class which mocks [PlatformWebViewController].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockPlatformWebViewController extends _i1.Mock
- implements _i4.PlatformWebViewController {
- MockPlatformWebViewController() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i2.PlatformWebViewControllerCreationParams get params =>
- (super.noSuchMethod(Invocation.getter(#params),
- returnValue: _FakePlatformWebViewControllerCreationParams_0(
- this, Invocation.getter(#params)))
- as _i2.PlatformWebViewControllerCreationParams);
- @override
- _i5.Future<void> loadFile(String? absoluteFilePath) => (super.noSuchMethod(
- Invocation.method(#loadFile, [absoluteFilePath]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<void> loadFlutterAsset(String? key) => (super.noSuchMethod(
- Invocation.method(#loadFlutterAsset, [key]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<void> loadHtmlString(String? html, {String? baseUrl}) =>
- (super.noSuchMethod(
- Invocation.method(#loadHtmlString, [html], {#baseUrl: baseUrl}),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value())
- as _i5.Future<void>);
- @override
- _i5.Future<void> loadRequest(_i2.LoadRequestParams? params) =>
- (super.noSuchMethod(Invocation.method(#loadRequest, [params]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value())
- as _i5.Future<void>);
- @override
- _i5.Future<String?> currentUrl() =>
- (super.noSuchMethod(Invocation.method(#currentUrl, []),
- returnValue: _i5.Future<String?>.value()) as _i5.Future<String?>);
- @override
- _i5.Future<bool> canGoBack() =>
- (super.noSuchMethod(Invocation.method(#canGoBack, []),
- returnValue: _i5.Future<bool>.value(false)) as _i5.Future<bool>);
- @override
- _i5.Future<bool> canGoForward() =>
- (super.noSuchMethod(Invocation.method(#canGoForward, []),
- returnValue: _i5.Future<bool>.value(false)) as _i5.Future<bool>);
- @override
- _i5.Future<void> goBack() => (super.noSuchMethod(
- Invocation.method(#goBack, []),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<void> goForward() => (super.noSuchMethod(
- Invocation.method(#goForward, []),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<void> reload() => (super.noSuchMethod(
- Invocation.method(#reload, []),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<void> clearCache() => (super.noSuchMethod(
- Invocation.method(#clearCache, []),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<void> clearLocalStorage() => (super.noSuchMethod(
- Invocation.method(#clearLocalStorage, []),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<void> setPlatformNavigationDelegate(
- _i6.PlatformNavigationDelegate? handler) =>
- (super.noSuchMethod(
- Invocation.method(#setPlatformNavigationDelegate, [handler]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value())
- as _i5.Future<void>);
- @override
- _i5.Future<void> runJavaScript(String? javaScript) => (super.noSuchMethod(
- Invocation.method(#runJavaScript, [javaScript]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<String> runJavaScriptReturningResult(String? javaScript) =>
- (super.noSuchMethod(
- Invocation.method(#runJavaScriptReturningResult, [javaScript]),
- returnValue: _i5.Future<String>.value('')) as _i5.Future<String>);
- @override
- _i5.Future<void> addJavaScriptChannel(
- _i4.JavaScriptChannelParams? javaScriptChannelParams) =>
- (super.noSuchMethod(
- Invocation.method(#addJavaScriptChannel, [javaScriptChannelParams]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub:
- _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<void> removeJavaScriptChannel(String? javaScriptChannelName) =>
- (super.noSuchMethod(
- Invocation.method(#removeJavaScriptChannel, [javaScriptChannelName]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub:
- _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<String?> getTitle() =>
- (super.noSuchMethod(Invocation.method(#getTitle, []),
- returnValue: _i5.Future<String?>.value()) as _i5.Future<String?>);
- @override
- _i5.Future<void> scrollTo(int? x, int? y) => (super.noSuchMethod(
- Invocation.method(#scrollTo, [x, y]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<void> scrollBy(int? x, int? y) => (super.noSuchMethod(
- Invocation.method(#scrollBy, [x, y]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<_i3.Point<int>> getScrollPosition() =>
- (super.noSuchMethod(Invocation.method(#getScrollPosition, []),
- returnValue: _i5.Future<_i3.Point<int>>.value(_FakePoint_1<int>(
- this, Invocation.method(#getScrollPosition, []))))
- as _i5.Future<_i3.Point<int>>);
- @override
- _i5.Future<void> enableDebugging(bool? enabled) => (super.noSuchMethod(
- Invocation.method(#enableDebugging, [enabled]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<void> enableGestureNavigation(bool? enabled) => (super
- .noSuchMethod(Invocation.method(#enableGestureNavigation, [enabled]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value())
- as _i5.Future<void>);
- @override
- _i5.Future<void> enableZoom(bool? enabled) => (super.noSuchMethod(
- Invocation.method(#enableZoom, [enabled]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<void> setBackgroundColor(_i7.Color? color) => (super.noSuchMethod(
- Invocation.method(#setBackgroundColor, [color]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value()) as _i5.Future<void>);
- @override
- _i5.Future<void> setJavaScriptMode(_i2.JavaScriptMode? javaScriptMode) =>
- (super.noSuchMethod(
- Invocation.method(#setJavaScriptMode, [javaScriptMode]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value())
- as _i5.Future<void>);
- @override
- _i5.Future<void> setUserAgent(String? userAgent) => (super.noSuchMethod(
- Invocation.method(#setUserAgent, [userAgent]),
- returnValue: _i5.Future<void>.value(),
- returnValueForMissingStub: _i5.Future<void>.value()) as _i5.Future<void>);
-}
diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.mocks.dart
deleted file mode 100644
index 4bca8b6..0000000
--- a/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.mocks.dart
+++ /dev/null
@@ -1,56 +0,0 @@
-// Mocks generated by Mockito 5.3.0 from annotations
-// in webview_flutter/test/v4/webview_cookie_manager_test.dart.
-// Do not manually edit this file.
-
-// ignore_for_file: no_leading_underscores_for_library_prefixes
-import 'dart:async' as _i4;
-
-import 'package:mockito/mockito.dart' as _i1;
-import 'package:webview_flutter_platform_interface/v4/src/platform_webview_cookie_manager.dart'
- as _i3;
-import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart'
- as _i2;
-
-// ignore_for_file: type=lint
-// ignore_for_file: avoid_redundant_argument_values
-// ignore_for_file: avoid_setters_without_getters
-// ignore_for_file: comment_references
-// ignore_for_file: implementation_imports
-// ignore_for_file: invalid_use_of_visible_for_testing_member
-// ignore_for_file: prefer_const_constructors
-// ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: camel_case_types
-// ignore_for_file: subtype_of_sealed_class
-
-class _FakePlatformWebViewCookieManagerCreationParams_0 extends _i1.SmartFake
- implements _i2.PlatformWebViewCookieManagerCreationParams {
- _FakePlatformWebViewCookieManagerCreationParams_0(
- Object parent, Invocation parentInvocation)
- : super(parent, parentInvocation);
-}
-
-/// A class which mocks [PlatformWebViewCookieManager].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockPlatformWebViewCookieManager extends _i1.Mock
- implements _i3.PlatformWebViewCookieManager {
- MockPlatformWebViewCookieManager() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i2.PlatformWebViewCookieManagerCreationParams get params =>
- (super.noSuchMethod(Invocation.getter(#params),
- returnValue: _FakePlatformWebViewCookieManagerCreationParams_0(
- this, Invocation.getter(#params)))
- as _i2.PlatformWebViewCookieManagerCreationParams);
- @override
- _i4.Future<bool> clearCookies() =>
- (super.noSuchMethod(Invocation.method(#clearCookies, []),
- returnValue: _i4.Future<bool>.value(false)) as _i4.Future<bool>);
- @override
- _i4.Future<void> setCookie(_i2.WebViewCookie? cookie) => (super.noSuchMethod(
- Invocation.method(#setCookie, [cookie]),
- returnValue: _i4.Future<void>.value(),
- returnValueForMissingStub: _i4.Future<void>.value()) as _i4.Future<void>);
-}
diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.mocks.dart
deleted file mode 100644
index e481d75..0000000
--- a/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.mocks.dart
+++ /dev/null
@@ -1,246 +0,0 @@
-// Mocks generated by Mockito 5.3.0 from annotations
-// in webview_flutter/test/v4/webview_widget_test.dart.
-// Do not manually edit this file.
-
-// ignore_for_file: no_leading_underscores_for_library_prefixes
-import 'dart:async' as _i7;
-import 'dart:math' as _i3;
-import 'dart:ui' as _i9;
-
-import 'package:flutter/foundation.dart' as _i5;
-import 'package:flutter/widgets.dart' as _i4;
-import 'package:mockito/mockito.dart' as _i1;
-import 'package:webview_flutter_platform_interface/v4/src/platform_navigation_delegate.dart'
- as _i8;
-import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart'
- as _i6;
-import 'package:webview_flutter_platform_interface/v4/src/platform_webview_widget.dart'
- as _i10;
-import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart'
- as _i2;
-
-// ignore_for_file: type=lint
-// ignore_for_file: avoid_redundant_argument_values
-// ignore_for_file: avoid_setters_without_getters
-// ignore_for_file: comment_references
-// ignore_for_file: implementation_imports
-// ignore_for_file: invalid_use_of_visible_for_testing_member
-// ignore_for_file: prefer_const_constructors
-// ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: camel_case_types
-// ignore_for_file: subtype_of_sealed_class
-
-class _FakePlatformWebViewControllerCreationParams_0 extends _i1.SmartFake
- implements _i2.PlatformWebViewControllerCreationParams {
- _FakePlatformWebViewControllerCreationParams_0(
- Object parent, Invocation parentInvocation)
- : super(parent, parentInvocation);
-}
-
-class _FakePoint_1<T extends num> extends _i1.SmartFake
- implements _i3.Point<T> {
- _FakePoint_1(Object parent, Invocation parentInvocation)
- : super(parent, parentInvocation);
-}
-
-class _FakePlatformWebViewWidgetCreationParams_2 extends _i1.SmartFake
- implements _i2.PlatformWebViewWidgetCreationParams {
- _FakePlatformWebViewWidgetCreationParams_2(
- Object parent, Invocation parentInvocation)
- : super(parent, parentInvocation);
-}
-
-class _FakeWidget_3 extends _i1.SmartFake implements _i4.Widget {
- _FakeWidget_3(Object parent, Invocation parentInvocation)
- : super(parent, parentInvocation);
-
- @override
- String toString({_i5.DiagnosticLevel? minLevel = _i5.DiagnosticLevel.info}) =>
- super.toString();
-}
-
-/// A class which mocks [PlatformWebViewController].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockPlatformWebViewController extends _i1.Mock
- implements _i6.PlatformWebViewController {
- MockPlatformWebViewController() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i2.PlatformWebViewControllerCreationParams get params =>
- (super.noSuchMethod(Invocation.getter(#params),
- returnValue: _FakePlatformWebViewControllerCreationParams_0(
- this, Invocation.getter(#params)))
- as _i2.PlatformWebViewControllerCreationParams);
- @override
- _i7.Future<void> loadFile(String? absoluteFilePath) => (super.noSuchMethod(
- Invocation.method(#loadFile, [absoluteFilePath]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<void> loadFlutterAsset(String? key) => (super.noSuchMethod(
- Invocation.method(#loadFlutterAsset, [key]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<void> loadHtmlString(String? html, {String? baseUrl}) =>
- (super.noSuchMethod(
- Invocation.method(#loadHtmlString, [html], {#baseUrl: baseUrl}),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value())
- as _i7.Future<void>);
- @override
- _i7.Future<void> loadRequest(_i2.LoadRequestParams? params) =>
- (super.noSuchMethod(Invocation.method(#loadRequest, [params]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value())
- as _i7.Future<void>);
- @override
- _i7.Future<String?> currentUrl() =>
- (super.noSuchMethod(Invocation.method(#currentUrl, []),
- returnValue: _i7.Future<String?>.value()) as _i7.Future<String?>);
- @override
- _i7.Future<bool> canGoBack() =>
- (super.noSuchMethod(Invocation.method(#canGoBack, []),
- returnValue: _i7.Future<bool>.value(false)) as _i7.Future<bool>);
- @override
- _i7.Future<bool> canGoForward() =>
- (super.noSuchMethod(Invocation.method(#canGoForward, []),
- returnValue: _i7.Future<bool>.value(false)) as _i7.Future<bool>);
- @override
- _i7.Future<void> goBack() => (super.noSuchMethod(
- Invocation.method(#goBack, []),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<void> goForward() => (super.noSuchMethod(
- Invocation.method(#goForward, []),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<void> reload() => (super.noSuchMethod(
- Invocation.method(#reload, []),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<void> clearCache() => (super.noSuchMethod(
- Invocation.method(#clearCache, []),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<void> clearLocalStorage() => (super.noSuchMethod(
- Invocation.method(#clearLocalStorage, []),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<void> setPlatformNavigationDelegate(
- _i8.PlatformNavigationDelegate? handler) =>
- (super.noSuchMethod(
- Invocation.method(#setPlatformNavigationDelegate, [handler]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value())
- as _i7.Future<void>);
- @override
- _i7.Future<void> runJavaScript(String? javaScript) => (super.noSuchMethod(
- Invocation.method(#runJavaScript, [javaScript]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<String> runJavaScriptReturningResult(String? javaScript) =>
- (super.noSuchMethod(
- Invocation.method(#runJavaScriptReturningResult, [javaScript]),
- returnValue: _i7.Future<String>.value('')) as _i7.Future<String>);
- @override
- _i7.Future<void> addJavaScriptChannel(
- _i6.JavaScriptChannelParams? javaScriptChannelParams) =>
- (super.noSuchMethod(
- Invocation.method(#addJavaScriptChannel, [javaScriptChannelParams]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub:
- _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<void> removeJavaScriptChannel(String? javaScriptChannelName) =>
- (super.noSuchMethod(
- Invocation.method(#removeJavaScriptChannel, [javaScriptChannelName]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub:
- _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<String?> getTitle() =>
- (super.noSuchMethod(Invocation.method(#getTitle, []),
- returnValue: _i7.Future<String?>.value()) as _i7.Future<String?>);
- @override
- _i7.Future<void> scrollTo(int? x, int? y) => (super.noSuchMethod(
- Invocation.method(#scrollTo, [x, y]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<void> scrollBy(int? x, int? y) => (super.noSuchMethod(
- Invocation.method(#scrollBy, [x, y]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<_i3.Point<int>> getScrollPosition() =>
- (super.noSuchMethod(Invocation.method(#getScrollPosition, []),
- returnValue: _i7.Future<_i3.Point<int>>.value(_FakePoint_1<int>(
- this, Invocation.method(#getScrollPosition, []))))
- as _i7.Future<_i3.Point<int>>);
- @override
- _i7.Future<void> enableDebugging(bool? enabled) => (super.noSuchMethod(
- Invocation.method(#enableDebugging, [enabled]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<void> enableGestureNavigation(bool? enabled) => (super
- .noSuchMethod(Invocation.method(#enableGestureNavigation, [enabled]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value())
- as _i7.Future<void>);
- @override
- _i7.Future<void> enableZoom(bool? enabled) => (super.noSuchMethod(
- Invocation.method(#enableZoom, [enabled]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<void> setBackgroundColor(_i9.Color? color) => (super.noSuchMethod(
- Invocation.method(#setBackgroundColor, [color]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value()) as _i7.Future<void>);
- @override
- _i7.Future<void> setJavaScriptMode(_i2.JavaScriptMode? javaScriptMode) =>
- (super.noSuchMethod(
- Invocation.method(#setJavaScriptMode, [javaScriptMode]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value())
- as _i7.Future<void>);
- @override
- _i7.Future<void> setUserAgent(String? userAgent) => (super.noSuchMethod(
- Invocation.method(#setUserAgent, [userAgent]),
- returnValue: _i7.Future<void>.value(),
- returnValueForMissingStub: _i7.Future<void>.value()) as _i7.Future<void>);
-}
-
-/// A class which mocks [PlatformWebViewWidget].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockPlatformWebViewWidget extends _i1.Mock
- implements _i10.PlatformWebViewWidget {
- MockPlatformWebViewWidget() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i2.PlatformWebViewWidgetCreationParams get params =>
- (super.noSuchMethod(Invocation.getter(#params),
- returnValue: _FakePlatformWebViewWidgetCreationParams_2(
- this, Invocation.getter(#params)))
- as _i2.PlatformWebViewWidgetCreationParams);
- @override
- _i4.Widget build(_i4.BuildContext? context) =>
- (super.noSuchMethod(Invocation.method(#build, [context]),
- returnValue:
- _FakeWidget_3(this, Invocation.method(#build, [context])))
- as _i4.Widget);
-}
diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_controller_test.dart b/packages/webview_flutter/webview_flutter/test/webview_controller_test.dart
similarity index 91%
rename from packages/webview_flutter/webview_flutter/test/v4/webview_controller_test.dart
rename to packages/webview_flutter/webview_flutter/test/webview_controller_test.dart
index f767a2e..f11884b 100644
--- a/packages/webview_flutter/webview_flutter/test/v4/webview_controller_test.dart
+++ b/packages/webview_flutter/webview_flutter/test/webview_controller_test.dart
@@ -2,19 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'dart:math';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
-import 'package:webview_flutter/src/v4/src/webview_controller.dart';
-import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter/webview_flutter.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
import 'webview_controller_test.mocks.dart';
-@GenerateMocks(<Type>[PlatformWebViewController])
+@GenerateMocks(<Type>[PlatformWebViewController, PlatformNavigationDelegate])
void main() {
test('loadFile', () async {
final MockPlatformWebViewController mockPlatformWebViewController =
@@ -286,9 +285,7 @@
final MockPlatformWebViewController mockPlatformWebViewController =
MockPlatformWebViewController();
when(mockPlatformWebViewController.getScrollPosition()).thenAnswer(
- (_) => Future<Point<int>>.value(
- const Point<int>(2, 3),
- ),
+ (_) => Future<Offset>.value(const Offset(2, 3)),
);
final WebViewController webViewController = WebViewController.fromPlatform(
@@ -350,4 +347,22 @@
await webViewController.setUserAgent('userAgent');
verify(mockPlatformWebViewController.setUserAgent('userAgent'));
});
+
+ test('setNavigationDelegate', () async {
+ final MockPlatformWebViewController mockPlatformWebViewController =
+ MockPlatformWebViewController();
+ final WebViewController webViewController = WebViewController.fromPlatform(
+ mockPlatformWebViewController,
+ );
+
+ final MockPlatformNavigationDelegate mockPlatformNavigationDelegate =
+ MockPlatformNavigationDelegate();
+ final NavigationDelegate navigationDelegate =
+ NavigationDelegate.fromPlatform(mockPlatformNavigationDelegate);
+
+ await webViewController.setNavigationDelegate(navigationDelegate);
+ verify(mockPlatformWebViewController.setPlatformNavigationDelegate(
+ mockPlatformNavigationDelegate,
+ ));
+ });
}
diff --git a/packages/webview_flutter/webview_flutter/test/webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/webview_controller_test.mocks.dart
new file mode 100644
index 0000000..2bb1ef6
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter/test/webview_controller_test.mocks.dart
@@ -0,0 +1,417 @@
+// Mocks generated by Mockito 5.3.2 from annotations
+// in webview_flutter/test/webview_controller_test.dart.
+// Do not manually edit this file.
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:async' as _i5;
+import 'dart:ui' as _i3;
+
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:webview_flutter_platform_interface/src/platform_navigation_delegate.dart'
+ as _i6;
+import 'package:webview_flutter_platform_interface/src/platform_webview_controller.dart'
+ as _i4;
+import 'package:webview_flutter_platform_interface/src/webview_platform.dart'
+ as _i2;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+class _FakePlatformWebViewControllerCreationParams_0 extends _i1.SmartFake
+ implements _i2.PlatformWebViewControllerCreationParams {
+ _FakePlatformWebViewControllerCreationParams_0(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakeObject_1 extends _i1.SmartFake implements Object {
+ _FakeObject_1(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakeOffset_2 extends _i1.SmartFake implements _i3.Offset {
+ _FakeOffset_2(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakePlatformNavigationDelegateCreationParams_3 extends _i1.SmartFake
+ implements _i2.PlatformNavigationDelegateCreationParams {
+ _FakePlatformNavigationDelegateCreationParams_3(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+/// A class which mocks [PlatformWebViewController].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockPlatformWebViewController extends _i1.Mock
+ implements _i4.PlatformWebViewController {
+ MockPlatformWebViewController() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i2.PlatformWebViewControllerCreationParams get params => (super.noSuchMethod(
+ Invocation.getter(#params),
+ returnValue: _FakePlatformWebViewControllerCreationParams_0(
+ this,
+ Invocation.getter(#params),
+ ),
+ ) as _i2.PlatformWebViewControllerCreationParams);
+ @override
+ _i5.Future<void> loadFile(String? absoluteFilePath) => (super.noSuchMethod(
+ Invocation.method(
+ #loadFile,
+ [absoluteFilePath],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> loadFlutterAsset(String? key) => (super.noSuchMethod(
+ Invocation.method(
+ #loadFlutterAsset,
+ [key],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> loadHtmlString(
+ String? html, {
+ String? baseUrl,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #loadHtmlString,
+ [html],
+ {#baseUrl: baseUrl},
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> loadRequest(_i2.LoadRequestParams? params) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #loadRequest,
+ [params],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<String?> currentUrl() => (super.noSuchMethod(
+ Invocation.method(
+ #currentUrl,
+ [],
+ ),
+ returnValue: _i5.Future<String?>.value(),
+ ) as _i5.Future<String?>);
+ @override
+ _i5.Future<bool> canGoBack() => (super.noSuchMethod(
+ Invocation.method(
+ #canGoBack,
+ [],
+ ),
+ returnValue: _i5.Future<bool>.value(false),
+ ) as _i5.Future<bool>);
+ @override
+ _i5.Future<bool> canGoForward() => (super.noSuchMethod(
+ Invocation.method(
+ #canGoForward,
+ [],
+ ),
+ returnValue: _i5.Future<bool>.value(false),
+ ) as _i5.Future<bool>);
+ @override
+ _i5.Future<void> goBack() => (super.noSuchMethod(
+ Invocation.method(
+ #goBack,
+ [],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> goForward() => (super.noSuchMethod(
+ Invocation.method(
+ #goForward,
+ [],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> reload() => (super.noSuchMethod(
+ Invocation.method(
+ #reload,
+ [],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> clearCache() => (super.noSuchMethod(
+ Invocation.method(
+ #clearCache,
+ [],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> clearLocalStorage() => (super.noSuchMethod(
+ Invocation.method(
+ #clearLocalStorage,
+ [],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> setPlatformNavigationDelegate(
+ _i6.PlatformNavigationDelegate? handler) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setPlatformNavigationDelegate,
+ [handler],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> runJavaScript(String? javaScript) => (super.noSuchMethod(
+ Invocation.method(
+ #runJavaScript,
+ [javaScript],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<Object> runJavaScriptReturningResult(String? javaScript) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #runJavaScriptReturningResult,
+ [javaScript],
+ ),
+ returnValue: _i5.Future<Object>.value(_FakeObject_1(
+ this,
+ Invocation.method(
+ #runJavaScriptReturningResult,
+ [javaScript],
+ ),
+ )),
+ ) as _i5.Future<Object>);
+ @override
+ _i5.Future<void> addJavaScriptChannel(
+ _i4.JavaScriptChannelParams? javaScriptChannelParams) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #addJavaScriptChannel,
+ [javaScriptChannelParams],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> removeJavaScriptChannel(String? javaScriptChannelName) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #removeJavaScriptChannel,
+ [javaScriptChannelName],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<String?> getTitle() => (super.noSuchMethod(
+ Invocation.method(
+ #getTitle,
+ [],
+ ),
+ returnValue: _i5.Future<String?>.value(),
+ ) as _i5.Future<String?>);
+ @override
+ _i5.Future<void> scrollTo(
+ int? x,
+ int? y,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #scrollTo,
+ [
+ x,
+ y,
+ ],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> scrollBy(
+ int? x,
+ int? y,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #scrollBy,
+ [
+ x,
+ y,
+ ],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<_i3.Offset> getScrollPosition() => (super.noSuchMethod(
+ Invocation.method(
+ #getScrollPosition,
+ [],
+ ),
+ returnValue: _i5.Future<_i3.Offset>.value(_FakeOffset_2(
+ this,
+ Invocation.method(
+ #getScrollPosition,
+ [],
+ ),
+ )),
+ ) as _i5.Future<_i3.Offset>);
+ @override
+ _i5.Future<void> enableZoom(bool? enabled) => (super.noSuchMethod(
+ Invocation.method(
+ #enableZoom,
+ [enabled],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> setBackgroundColor(_i3.Color? color) => (super.noSuchMethod(
+ Invocation.method(
+ #setBackgroundColor,
+ [color],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> setJavaScriptMode(_i2.JavaScriptMode? javaScriptMode) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setJavaScriptMode,
+ [javaScriptMode],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> setUserAgent(String? userAgent) => (super.noSuchMethod(
+ Invocation.method(
+ #setUserAgent,
+ [userAgent],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+}
+
+/// A class which mocks [PlatformNavigationDelegate].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockPlatformNavigationDelegate extends _i1.Mock
+ implements _i6.PlatformNavigationDelegate {
+ MockPlatformNavigationDelegate() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i2.PlatformNavigationDelegateCreationParams get params =>
+ (super.noSuchMethod(
+ Invocation.getter(#params),
+ returnValue: _FakePlatformNavigationDelegateCreationParams_3(
+ this,
+ Invocation.getter(#params),
+ ),
+ ) as _i2.PlatformNavigationDelegateCreationParams);
+ @override
+ _i5.Future<void> setOnNavigationRequest(
+ _i6.NavigationRequestCallback? onNavigationRequest) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setOnNavigationRequest,
+ [onNavigationRequest],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> setOnPageStarted(_i6.PageEventCallback? onPageStarted) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setOnPageStarted,
+ [onPageStarted],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> setOnPageFinished(_i6.PageEventCallback? onPageFinished) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setOnPageFinished,
+ [onPageFinished],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> setOnProgress(_i6.ProgressCallback? onProgress) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setOnProgress,
+ [onProgress],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+ @override
+ _i5.Future<void> setOnWebResourceError(
+ _i6.WebResourceErrorCallback? onWebResourceError) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setOnWebResourceError,
+ [onWebResourceError],
+ ),
+ returnValue: _i5.Future<void>.value(),
+ returnValueForMissingStub: _i5.Future<void>.value(),
+ ) as _i5.Future<void>);
+}
diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter/test/webview_cookie_manager_test.dart
similarity index 90%
rename from packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.dart
rename to packages/webview_flutter/webview_flutter/test/webview_cookie_manager_test.dart
index e815240..babf74b 100644
--- a/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.dart
+++ b/packages/webview_flutter/webview_flutter/test/webview_cookie_manager_test.dart
@@ -5,8 +5,8 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
-import 'package:webview_flutter/src/v4/src/webview_cookie_manager.dart';
-import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter/webview_flutter.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
import 'webview_cookie_manager_test.mocks.dart';
diff --git a/packages/webview_flutter/webview_flutter/test/webview_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/webview_cookie_manager_test.mocks.dart
new file mode 100644
index 0000000..7cae663
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter/test/webview_cookie_manager_test.mocks.dart
@@ -0,0 +1,71 @@
+// Mocks generated by Mockito 5.3.2 from annotations
+// in webview_flutter/test/webview_cookie_manager_test.dart.
+// Do not manually edit this file.
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:async' as _i4;
+
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:webview_flutter_platform_interface/src/platform_webview_cookie_manager.dart'
+ as _i3;
+import 'package:webview_flutter_platform_interface/src/webview_platform.dart'
+ as _i2;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+class _FakePlatformWebViewCookieManagerCreationParams_0 extends _i1.SmartFake
+ implements _i2.PlatformWebViewCookieManagerCreationParams {
+ _FakePlatformWebViewCookieManagerCreationParams_0(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+/// A class which mocks [PlatformWebViewCookieManager].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockPlatformWebViewCookieManager extends _i1.Mock
+ implements _i3.PlatformWebViewCookieManager {
+ MockPlatformWebViewCookieManager() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i2.PlatformWebViewCookieManagerCreationParams get params =>
+ (super.noSuchMethod(
+ Invocation.getter(#params),
+ returnValue: _FakePlatformWebViewCookieManagerCreationParams_0(
+ this,
+ Invocation.getter(#params),
+ ),
+ ) as _i2.PlatformWebViewCookieManagerCreationParams);
+ @override
+ _i4.Future<bool> clearCookies() => (super.noSuchMethod(
+ Invocation.method(
+ #clearCookies,
+ [],
+ ),
+ returnValue: _i4.Future<bool>.value(false),
+ ) as _i4.Future<bool>);
+ @override
+ _i4.Future<void> setCookie(_i2.WebViewCookie? cookie) => (super.noSuchMethod(
+ Invocation.method(
+ #setCookie,
+ [cookie],
+ ),
+ returnValue: _i4.Future<void>.value(),
+ returnValueForMissingStub: _i4.Future<void>.value(),
+ ) as _i4.Future<void>);
+}
diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart
index b10366c..d13b51c 100644
--- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart
+++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart
@@ -2,1366 +2,43 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'dart:typed_data';
-
-import 'package:flutter/src/foundation/basic_types.dart';
-import 'package:flutter/src/gestures/recognizer.dart';
-import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
-import 'package:mockito/annotations.dart';
-import 'package:mockito/mockito.dart';
-import 'package:webview_flutter/webview_flutter.dart';
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter/webview_flutter.dart' as main_file;
-import 'webview_flutter_test.mocks.dart';
-
-typedef VoidCallback = void Function();
-
-@GenerateMocks(<Type>[WebViewPlatform, WebViewPlatformController])
void main() {
- TestWidgetsFlutterBinding.ensureInitialized();
-
- late MockWebViewPlatform mockWebViewPlatform;
- late MockWebViewPlatformController mockWebViewPlatformController;
- late MockWebViewCookieManagerPlatform mockWebViewCookieManagerPlatform;
-
- setUp(() {
- mockWebViewPlatformController = MockWebViewPlatformController();
- mockWebViewPlatform = MockWebViewPlatform();
- mockWebViewCookieManagerPlatform = MockWebViewCookieManagerPlatform();
- when(mockWebViewPlatform.build(
- context: anyNamed('context'),
- creationParams: anyNamed('creationParams'),
- webViewPlatformCallbacksHandler:
- anyNamed('webViewPlatformCallbacksHandler'),
- javascriptChannelRegistry: anyNamed('javascriptChannelRegistry'),
- onWebViewPlatformCreated: anyNamed('onWebViewPlatformCreated'),
- gestureRecognizers: anyNamed('gestureRecognizers'),
- )).thenAnswer((Invocation invocation) {
- final WebViewPlatformCreatedCallback onWebViewPlatformCreated =
- invocation.namedArguments[const Symbol('onWebViewPlatformCreated')]
- as WebViewPlatformCreatedCallback;
- return TestPlatformWebView(
- mockWebViewPlatformController: mockWebViewPlatformController,
- onWebViewPlatformCreated: onWebViewPlatformCreated,
- );
- });
-
- WebView.platform = mockWebViewPlatform;
- WebViewCookieManagerPlatform.instance = mockWebViewCookieManagerPlatform;
- });
-
- tearDown(() {
- mockWebViewCookieManagerPlatform.reset();
- });
-
- testWidgets('Create WebView', (WidgetTester tester) async {
- await tester.pumpWidget(const WebView());
- });
-
- testWidgets('Initial url', (WidgetTester tester) async {
- await tester.pumpWidget(const WebView(initialUrl: 'https://youtube.com'));
-
- final CreationParams params = captureBuildArgs(
- mockWebViewPlatform,
- creationParams: true,
- ).single as CreationParams;
-
- expect(params.initialUrl, 'https://youtube.com');
- });
-
- testWidgets('Javascript mode', (WidgetTester tester) async {
- await tester.pumpWidget(const WebView(
- javascriptMode: JavascriptMode.unrestricted,
- ));
-
- final CreationParams unrestrictedparams = captureBuildArgs(
- mockWebViewPlatform,
- creationParams: true,
- ).single as CreationParams;
-
- expect(
- unrestrictedparams.webSettings!.javascriptMode,
- JavascriptMode.unrestricted,
- );
-
- await tester.pumpWidget(const WebView());
-
- final CreationParams disabledparams = captureBuildArgs(
- mockWebViewPlatform,
- creationParams: true,
- ).single as CreationParams;
-
- expect(disabledparams.webSettings!.javascriptMode, JavascriptMode.disabled);
- });
-
- testWidgets('Load file', (WidgetTester tester) async {
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
-
- await controller!.loadFile('/test/path/index.html');
-
- verify(mockWebViewPlatformController.loadFile(
- '/test/path/index.html',
- ));
- });
-
- testWidgets('Load file with empty path', (WidgetTester tester) async {
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
-
- expect(() => controller!.loadFile(''), throwsAssertionError);
- });
-
- testWidgets('Load Flutter asset', (WidgetTester tester) async {
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
-
- await controller!.loadFlutterAsset('assets/index.html');
-
- verify(mockWebViewPlatformController.loadFlutterAsset(
- 'assets/index.html',
- ));
- });
-
- testWidgets('Load Flutter asset with empty key', (WidgetTester tester) async {
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
-
- expect(() => controller!.loadFlutterAsset(''), throwsAssertionError);
- });
-
- testWidgets('Load HTML string without base URL', (WidgetTester tester) async {
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
-
- await controller!.loadHtmlString('<p>This is a test paragraph.</p>');
-
- verify(mockWebViewPlatformController.loadHtmlString(
- '<p>This is a test paragraph.</p>',
- ));
- });
-
- testWidgets('Load HTML string with base URL', (WidgetTester tester) async {
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
-
- await controller!.loadHtmlString(
- '<p>This is a test paragraph.</p>',
- baseUrl: 'https://flutter.dev',
- );
-
- verify(mockWebViewPlatformController.loadHtmlString(
- '<p>This is a test paragraph.</p>',
- baseUrl: 'https://flutter.dev',
- ));
- });
-
- testWidgets('Load HTML string with empty string',
- (WidgetTester tester) async {
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
-
- expect(() => controller!.loadHtmlString(''), throwsAssertionError);
- });
-
- testWidgets('Load url', (WidgetTester tester) async {
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
-
- await controller!.loadUrl('https://flutter.io');
-
- verify(mockWebViewPlatformController.loadUrl(
- 'https://flutter.io',
- argThat(isNull),
- ));
- });
-
- testWidgets('Invalid urls', (WidgetTester tester) async {
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
-
- final CreationParams params = captureBuildArgs(
- mockWebViewPlatform,
- creationParams: true,
- ).single as CreationParams;
-
- expect(params.initialUrl, isNull);
-
- expect(() => controller!.loadUrl(''), throwsA(anything));
- expect(() => controller!.loadUrl('flutter.io'), throwsA(anything));
- });
-
- testWidgets('Headers in loadUrl', (WidgetTester tester) async {
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
-
- final Map<String, String> headers = <String, String>{
- 'CACHE-CONTROL': 'ABC'
- };
- await controller!.loadUrl('https://flutter.io', headers: headers);
-
- verify(mockWebViewPlatformController.loadUrl(
- 'https://flutter.io',
- <String, String>{'CACHE-CONTROL': 'ABC'},
- ));
- });
-
- testWidgets('loadRequest', (WidgetTester tester) async {
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
- expect(controller, isNotNull);
-
- final WebViewRequest req = WebViewRequest(
- uri: Uri.parse('https://flutter.dev'),
- method: WebViewRequestMethod.post,
- headers: <String, String>{'foo': 'bar'},
- body: Uint8List.fromList('Test Body'.codeUnits),
- );
-
- await controller!.loadRequest(req);
-
- verify(mockWebViewPlatformController.loadRequest(req));
- });
-
- testWidgets('Clear Cache', (WidgetTester tester) async {
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
-
- await controller!.clearCache();
-
- verify(mockWebViewPlatformController.clearCache());
- });
-
- testWidgets('Can go back', (WidgetTester tester) async {
- when(mockWebViewPlatformController.canGoBack())
- .thenAnswer((_) => Future<bool>.value(true));
-
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://flutter.io',
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
- expect(controller!.canGoBack(), completion(true));
- });
-
- testWidgets("Can't go forward", (WidgetTester tester) async {
- when(mockWebViewPlatformController.canGoForward())
- .thenAnswer((_) => Future<bool>.value(false));
-
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
- expect(controller!.canGoForward(), completion(false));
- });
-
- testWidgets('Go back', (WidgetTester tester) async {
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://youtube.com',
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- await controller!.goBack();
- verify(mockWebViewPlatformController.goBack());
- });
-
- testWidgets('Go forward', (WidgetTester tester) async {
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://youtube.com',
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
- await controller!.goForward();
- verify(mockWebViewPlatformController.goForward());
- });
-
- testWidgets('Current URL', (WidgetTester tester) async {
- when(mockWebViewPlatformController.currentUrl())
- .thenAnswer((_) => Future<String>.value('https://youtube.com'));
-
- WebViewController? controller;
- await tester.pumpWidget(
- WebView(
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(controller, isNotNull);
- expect(await controller!.currentUrl(), 'https://youtube.com');
- });
-
- testWidgets('Reload url', (WidgetTester tester) async {
- late WebViewController controller;
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://flutter.io',
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- await controller.reload();
- verify(mockWebViewPlatformController.reload());
- });
-
- testWidgets('evaluate Javascript', (WidgetTester tester) async {
- when(mockWebViewPlatformController.evaluateJavascript('fake js string'))
- .thenAnswer((_) => Future<String>.value('fake js string'));
-
- late WebViewController controller;
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://flutter.io',
- javascriptMode: JavascriptMode.unrestricted,
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- expect(
- // ignore: deprecated_member_use_from_same_package
- await controller.evaluateJavascript('fake js string'),
- 'fake js string',
- reason: 'should get the argument');
- });
-
- testWidgets('evaluate Javascript with JavascriptMode disabled',
- (WidgetTester tester) async {
- late WebViewController controller;
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://flutter.io',
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
- expect(
- // ignore: deprecated_member_use_from_same_package
- () => controller.evaluateJavascript('fake js string'),
- throwsA(anything),
- );
- });
-
- testWidgets('runJavaScript', (WidgetTester tester) async {
- late WebViewController controller;
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://flutter.io',
- javascriptMode: JavascriptMode.unrestricted,
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
- await controller.runJavascript('fake js string');
- verify(mockWebViewPlatformController.runJavascript('fake js string'));
- });
-
- testWidgets('runJavaScript with JavascriptMode disabled',
- (WidgetTester tester) async {
- late WebViewController controller;
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://flutter.io',
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
- expect(
- () => controller.runJavascript('fake js string'),
- throwsA(anything),
- );
- });
-
- testWidgets('runJavaScriptReturningResult', (WidgetTester tester) async {
- when(mockWebViewPlatformController
- .runJavascriptReturningResult('fake js string'))
- .thenAnswer((_) => Future<String>.value('fake js string'));
-
- late WebViewController controller;
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://flutter.io',
- javascriptMode: JavascriptMode.unrestricted,
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
- expect(await controller.runJavascriptReturningResult('fake js string'),
- 'fake js string',
- reason: 'should get the argument');
- });
-
- testWidgets('runJavaScriptReturningResult with JavascriptMode disabled',
- (WidgetTester tester) async {
- late WebViewController controller;
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://flutter.io',
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
- expect(
- () => controller.runJavascriptReturningResult('fake js string'),
- throwsA(anything),
- );
- });
-
- testWidgets('Cookies can be cleared once', (WidgetTester tester) async {
- await tester.pumpWidget(
- const WebView(
- initialUrl: 'https://flutter.io',
- ),
- );
- final CookieManager cookieManager = CookieManager();
- final bool hasCookies = await cookieManager.clearCookies();
- expect(hasCookies, true);
- });
-
- testWidgets('Cookies can be set', (WidgetTester tester) async {
- const WebViewCookie cookie =
- WebViewCookie(name: 'foo', value: 'bar', domain: 'flutter.dev');
-
- await tester.pumpWidget(
- const WebView(
- initialUrl: 'https://flutter.io',
- ),
- );
- final CookieManager cookieManager = CookieManager();
- await cookieManager.setCookie(cookie);
- expect(mockWebViewCookieManagerPlatform.setCookieCalls,
- <WebViewCookie>[cookie]);
- });
-
- testWidgets('Initial JavaScript channels', (WidgetTester tester) async {
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://youtube.com',
- javascriptChannels: <JavascriptChannel>{
- JavascriptChannel(
- name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
- JavascriptChannel(
- name: 'Alarm', onMessageReceived: (JavascriptMessage msg) {}),
- },
- ),
- );
-
- final CreationParams params = captureBuildArgs(
- mockWebViewPlatform,
- creationParams: true,
- ).single as CreationParams;
-
- expect(params.javascriptChannelNames,
- unorderedEquals(<String>['Tts', 'Alarm']));
- });
-
- test('Only valid JavaScript channel names are allowed', () {
- void noOp(JavascriptMessage msg) {}
- JavascriptChannel(name: 'Tts1', onMessageReceived: noOp);
- JavascriptChannel(name: '_Alarm', onMessageReceived: noOp);
- JavascriptChannel(name: 'foo_bar_', onMessageReceived: noOp);
-
- VoidCallback createChannel(String name) {
- return () {
- JavascriptChannel(name: name, onMessageReceived: noOp);
- };
- }
-
- expect(createChannel('1Alarm'), throwsAssertionError);
- expect(createChannel('foo.bar'), throwsAssertionError);
- expect(createChannel(''), throwsAssertionError);
- });
-
- testWidgets('Unique JavaScript channel names are required',
- (WidgetTester tester) async {
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://youtube.com',
- javascriptChannels: <JavascriptChannel>{
- JavascriptChannel(
- name: 'Alarm', onMessageReceived: (JavascriptMessage msg) {}),
- JavascriptChannel(
- name: 'Alarm', onMessageReceived: (JavascriptMessage msg) {}),
- },
- ),
- );
- expect(tester.takeException(), isNot(null));
- });
-
- testWidgets('JavaScript channels update', (WidgetTester tester) async {
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://youtube.com',
- javascriptChannels: <JavascriptChannel>{
- JavascriptChannel(
- name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
- JavascriptChannel(
- name: 'Alarm', onMessageReceived: (JavascriptMessage msg) {}),
- },
- ),
- );
-
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://youtube.com',
- javascriptChannels: <JavascriptChannel>{
- JavascriptChannel(
- name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
- JavascriptChannel(
- name: 'Alarm2', onMessageReceived: (JavascriptMessage msg) {}),
- JavascriptChannel(
- name: 'Alarm3', onMessageReceived: (JavascriptMessage msg) {}),
- },
- ),
- );
-
- final JavascriptChannelRegistry channelRegistry = captureBuildArgs(
- mockWebViewPlatform,
- javascriptChannelRegistry: true,
- ).first as JavascriptChannelRegistry;
-
- expect(
- channelRegistry.channels.keys,
- unorderedEquals(<String>['Tts', 'Alarm2', 'Alarm3']),
- );
- });
-
- testWidgets('Remove all JavaScript channels and then add',
- (WidgetTester tester) async {
- // This covers a specific bug we had where after updating javascriptChannels to null,
- // updating it again with a subset of the previously registered channels fails as the
- // widget's cache of current channel wasn't properly updated when updating javascriptChannels to
- // null.
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://youtube.com',
- javascriptChannels: <JavascriptChannel>{
- JavascriptChannel(
- name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
- },
- ),
- );
-
- await tester.pumpWidget(
- const WebView(
- initialUrl: 'https://youtube.com',
- ),
- );
-
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://youtube.com',
- javascriptChannels: <JavascriptChannel>{
- JavascriptChannel(
- name: 'Tts', onMessageReceived: (JavascriptMessage msg) {}),
- },
- ),
- );
-
- final JavascriptChannelRegistry channelRegistry = captureBuildArgs(
- mockWebViewPlatform,
- javascriptChannelRegistry: true,
- ).last as JavascriptChannelRegistry;
-
- expect(channelRegistry.channels.keys, unorderedEquals(<String>['Tts']));
- });
-
- testWidgets('JavaScript channel messages', (WidgetTester tester) async {
- final List<String> ttsMessagesReceived = <String>[];
- final List<String> alarmMessagesReceived = <String>[];
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://youtube.com',
- javascriptChannels: <JavascriptChannel>{
- JavascriptChannel(
- name: 'Tts',
- onMessageReceived: (JavascriptMessage msg) {
- ttsMessagesReceived.add(msg.message);
- }),
- JavascriptChannel(
- name: 'Alarm',
- onMessageReceived: (JavascriptMessage msg) {
- alarmMessagesReceived.add(msg.message);
- }),
- },
- ),
- );
-
- final JavascriptChannelRegistry channelRegistry = captureBuildArgs(
- mockWebViewPlatform,
- javascriptChannelRegistry: true,
- ).single as JavascriptChannelRegistry;
-
- expect(ttsMessagesReceived, isEmpty);
- expect(alarmMessagesReceived, isEmpty);
-
- channelRegistry.onJavascriptChannelMessage('Tts', 'Hello');
- channelRegistry.onJavascriptChannelMessage('Tts', 'World');
-
- expect(ttsMessagesReceived, <String>['Hello', 'World']);
- });
-
- group('$PageStartedCallback', () {
- testWidgets('onPageStarted is not null', (WidgetTester tester) async {
- String? returnedUrl;
-
- await tester.pumpWidget(WebView(
- initialUrl: 'https://youtube.com',
- onPageStarted: (String url) {
- returnedUrl = url;
- },
- ));
-
- final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
- mockWebViewPlatform,
- webViewPlatformCallbacksHandler: true,
- ).single as WebViewPlatformCallbacksHandler;
-
- handler.onPageStarted('https://youtube.com');
-
- expect(returnedUrl, 'https://youtube.com');
- });
-
- testWidgets('onPageStarted is null', (WidgetTester tester) async {
- await tester.pumpWidget(const WebView(
- initialUrl: 'https://youtube.com',
- ));
-
- final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
- mockWebViewPlatform,
- webViewPlatformCallbacksHandler: true,
- ).single as WebViewPlatformCallbacksHandler;
-
- // The platform side will always invoke a call for onPageStarted. This is
- // to test that it does not crash on a null callback.
- handler.onPageStarted('https://youtube.com');
- });
-
- testWidgets('onPageStarted changed', (WidgetTester tester) async {
- String? returnedUrl;
-
- await tester.pumpWidget(WebView(
- initialUrl: 'https://youtube.com',
- onPageStarted: (String url) {},
- ));
-
- await tester.pumpWidget(WebView(
- initialUrl: 'https://youtube.com',
- onPageStarted: (String url) {
- returnedUrl = url;
- },
- ));
-
- final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
- mockWebViewPlatform,
- webViewPlatformCallbacksHandler: true,
- ).last as WebViewPlatformCallbacksHandler;
- handler.onPageStarted('https://youtube.com');
-
- expect(returnedUrl, 'https://youtube.com');
+ group('webview_flutter', () {
+ test('ensure webview_flutter.dart exports classes from platform interface',
+ () {
+ // ignore: unnecessary_statements
+ main_file.JavaScriptMessage;
+ // ignore: unnecessary_statements
+ main_file.JavaScriptMode;
+ // ignore: unnecessary_statements
+ main_file.LoadRequestMethod;
+ // ignore: unnecessary_statements
+ main_file.NavigationDecision;
+ // ignore: unnecessary_statements
+ main_file.NavigationRequest;
+ // ignore: unnecessary_statements
+ main_file.NavigationRequestCallback;
+ // ignore: unnecessary_statements
+ main_file.PageEventCallback;
+ // ignore: unnecessary_statements
+ main_file.PlatformNavigationDelegateCreationParams;
+ // ignore: unnecessary_statements
+ main_file.PlatformWebViewControllerCreationParams;
+ // ignore: unnecessary_statements
+ main_file.PlatformWebViewCookieManagerCreationParams;
+ // ignore: unnecessary_statements
+ main_file.PlatformWebViewWidgetCreationParams;
+ // ignore: unnecessary_statements
+ main_file.ProgressCallback;
+ // ignore: unnecessary_statements
+ main_file.WebResourceError;
+ // ignore: unnecessary_statements
+ main_file.WebResourceErrorCallback;
+ // ignore: unnecessary_statements
+ main_file.WebViewCookie;
});
});
-
- group('$PageFinishedCallback', () {
- testWidgets('onPageFinished is not null', (WidgetTester tester) async {
- String? returnedUrl;
-
- await tester.pumpWidget(WebView(
- initialUrl: 'https://youtube.com',
- onPageFinished: (String url) {
- returnedUrl = url;
- },
- ));
-
- final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
- mockWebViewPlatform,
- webViewPlatformCallbacksHandler: true,
- ).single as WebViewPlatformCallbacksHandler;
- handler.onPageFinished('https://youtube.com');
-
- expect(returnedUrl, 'https://youtube.com');
- });
-
- testWidgets('onPageFinished is null', (WidgetTester tester) async {
- await tester.pumpWidget(const WebView(
- initialUrl: 'https://youtube.com',
- ));
-
- final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
- mockWebViewPlatform,
- webViewPlatformCallbacksHandler: true,
- ).single as WebViewPlatformCallbacksHandler;
- // The platform side will always invoke a call for onPageFinished. This is
- // to test that it does not crash on a null callback.
- handler.onPageFinished('https://youtube.com');
- });
-
- testWidgets('onPageFinished changed', (WidgetTester tester) async {
- String? returnedUrl;
-
- await tester.pumpWidget(WebView(
- initialUrl: 'https://youtube.com',
- onPageFinished: (String url) {},
- ));
-
- await tester.pumpWidget(WebView(
- initialUrl: 'https://youtube.com',
- onPageFinished: (String url) {
- returnedUrl = url;
- },
- ));
-
- final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
- mockWebViewPlatform,
- webViewPlatformCallbacksHandler: true,
- ).last as WebViewPlatformCallbacksHandler;
- handler.onPageFinished('https://youtube.com');
-
- expect(returnedUrl, 'https://youtube.com');
- });
- });
-
- group('$PageLoadingCallback', () {
- testWidgets('onLoadingProgress is not null', (WidgetTester tester) async {
- int? loadingProgress;
-
- await tester.pumpWidget(WebView(
- initialUrl: 'https://youtube.com',
- onProgress: (int progress) {
- loadingProgress = progress;
- },
- ));
-
- final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
- mockWebViewPlatform,
- webViewPlatformCallbacksHandler: true,
- ).single as WebViewPlatformCallbacksHandler;
- handler.onProgress(50);
-
- expect(loadingProgress, 50);
- });
-
- testWidgets('onLoadingProgress is null', (WidgetTester tester) async {
- await tester.pumpWidget(const WebView(
- initialUrl: 'https://youtube.com',
- ));
-
- final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
- mockWebViewPlatform,
- webViewPlatformCallbacksHandler: true,
- ).single as WebViewPlatformCallbacksHandler;
-
- // This is to test that it does not crash on a null callback.
- handler.onProgress(50);
- });
-
- testWidgets('onLoadingProgress changed', (WidgetTester tester) async {
- int? loadingProgress;
-
- await tester.pumpWidget(WebView(
- initialUrl: 'https://youtube.com',
- onProgress: (int progress) {},
- ));
-
- await tester.pumpWidget(WebView(
- initialUrl: 'https://youtube.com',
- onProgress: (int progress) {
- loadingProgress = progress;
- },
- ));
-
- final WebViewPlatformCallbacksHandler handler = captureBuildArgs(
- mockWebViewPlatform,
- webViewPlatformCallbacksHandler: true,
- ).last as WebViewPlatformCallbacksHandler;
- handler.onProgress(50);
-
- expect(loadingProgress, 50);
- });
- });
-
- group('navigationDelegate', () {
- testWidgets('hasNavigationDelegate', (WidgetTester tester) async {
- await tester.pumpWidget(const WebView(
- initialUrl: 'https://youtube.com',
- ));
-
- final CreationParams params = captureBuildArgs(
- mockWebViewPlatform,
- creationParams: true,
- ).single as CreationParams;
-
- expect(params.webSettings!.hasNavigationDelegate, false);
-
- await tester.pumpWidget(WebView(
- initialUrl: 'https://youtube.com',
- navigationDelegate: (NavigationRequest r) =>
- NavigationDecision.navigate,
- ));
-
- final WebSettings updateSettings =
- verify(mockWebViewPlatformController.updateSettings(captureAny))
- .captured
- .single as WebSettings;
-
- expect(updateSettings.hasNavigationDelegate, true);
- });
-
- testWidgets('Block navigation', (WidgetTester tester) async {
- final List<NavigationRequest> navigationRequests = <NavigationRequest>[];
-
- await tester.pumpWidget(WebView(
- initialUrl: 'https://youtube.com',
- navigationDelegate: (NavigationRequest request) {
- navigationRequests.add(request);
- // Only allow navigating to https://flutter.dev
- return request.url == 'https://flutter.dev'
- ? NavigationDecision.navigate
- : NavigationDecision.prevent;
- }));
-
- final List<dynamic> args = captureBuildArgs(
- mockWebViewPlatform,
- creationParams: true,
- webViewPlatformCallbacksHandler: true,
- );
-
- final CreationParams params = args[0] as CreationParams;
- expect(params.webSettings!.hasNavigationDelegate, true);
-
- final WebViewPlatformCallbacksHandler handler =
- args[1] as WebViewPlatformCallbacksHandler;
-
- // The navigation delegate only allows navigation to https://flutter.dev
- // so we should still be in https://youtube.com.
- expect(
- handler.onNavigationRequest(
- url: 'https://www.google.com',
- isForMainFrame: true,
- ),
- completion(false),
- );
-
- expect(navigationRequests.length, 1);
- expect(navigationRequests[0].url, 'https://www.google.com');
- expect(navigationRequests[0].isForMainFrame, true);
-
- expect(
- handler.onNavigationRequest(
- url: 'https://flutter.dev',
- isForMainFrame: true,
- ),
- completion(true),
- );
- });
- });
-
- group('debuggingEnabled', () {
- testWidgets('enable debugging', (WidgetTester tester) async {
- await tester.pumpWidget(const WebView(
- debuggingEnabled: true,
- ));
-
- final CreationParams params = captureBuildArgs(
- mockWebViewPlatform,
- creationParams: true,
- ).single as CreationParams;
-
- expect(params.webSettings!.debuggingEnabled, true);
- });
-
- testWidgets('defaults to false', (WidgetTester tester) async {
- await tester.pumpWidget(const WebView());
-
- final CreationParams params = captureBuildArgs(
- mockWebViewPlatform,
- creationParams: true,
- ).single as CreationParams;
-
- expect(params.webSettings!.debuggingEnabled, false);
- });
-
- testWidgets('can be changed', (WidgetTester tester) async {
- final GlobalKey key = GlobalKey();
- await tester.pumpWidget(WebView(key: key));
-
- await tester.pumpWidget(WebView(
- key: key,
- debuggingEnabled: true,
- ));
-
- final WebSettings enabledSettings =
- verify(mockWebViewPlatformController.updateSettings(captureAny))
- .captured
- .last as WebSettings;
- expect(enabledSettings.debuggingEnabled, true);
-
- await tester.pumpWidget(WebView(
- key: key,
- ));
-
- final WebSettings disabledSettings =
- verify(mockWebViewPlatformController.updateSettings(captureAny))
- .captured
- .last as WebSettings;
- expect(disabledSettings.debuggingEnabled, false);
- });
- });
-
- group('zoomEnabled', () {
- testWidgets('Enable zoom', (WidgetTester tester) async {
- await tester.pumpWidget(const WebView());
-
- final CreationParams params = captureBuildArgs(
- mockWebViewPlatform,
- creationParams: true,
- ).single as CreationParams;
-
- expect(params.webSettings!.zoomEnabled, isTrue);
- });
-
- testWidgets('defaults to true', (WidgetTester tester) async {
- await tester.pumpWidget(const WebView());
-
- final CreationParams params = captureBuildArgs(
- mockWebViewPlatform,
- creationParams: true,
- ).single as CreationParams;
-
- expect(params.webSettings!.zoomEnabled, isTrue);
- });
-
- testWidgets('can be changed', (WidgetTester tester) async {
- final GlobalKey key = GlobalKey();
- await tester.pumpWidget(WebView(key: key));
-
- await tester.pumpWidget(WebView(
- key: key,
- ));
-
- final WebSettings enabledSettings =
- verify(mockWebViewPlatformController.updateSettings(captureAny))
- .captured
- .last as WebSettings;
- // Zoom defaults to true, so no changes are made to settings.
- expect(enabledSettings.zoomEnabled, isNull);
-
- await tester.pumpWidget(WebView(
- key: key,
- zoomEnabled: false,
- ));
-
- final WebSettings disabledSettings =
- verify(mockWebViewPlatformController.updateSettings(captureAny))
- .captured
- .last as WebSettings;
- expect(disabledSettings.zoomEnabled, isFalse);
- });
- });
-
- group('Background color', () {
- testWidgets('Defaults to null', (WidgetTester tester) async {
- await tester.pumpWidget(const WebView());
-
- final CreationParams params = captureBuildArgs(
- mockWebViewPlatform,
- creationParams: true,
- ).single as CreationParams;
-
- expect(params.backgroundColor, null);
- });
-
- testWidgets('Can be transparent', (WidgetTester tester) async {
- const Color transparentColor = Color(0x00000000);
-
- await tester.pumpWidget(const WebView(
- backgroundColor: transparentColor,
- ));
-
- final CreationParams params = captureBuildArgs(
- mockWebViewPlatform,
- creationParams: true,
- ).single as CreationParams;
-
- expect(params.backgroundColor, transparentColor);
- });
- });
-
- group('Custom platform implementation', () {
- setUp(() {
- WebView.platform = MyWebViewPlatform();
- });
- tearDownAll(() {
- WebView.platform = null;
- });
-
- testWidgets('creation', (WidgetTester tester) async {
- await tester.pumpWidget(
- const WebView(
- initialUrl: 'https://youtube.com',
- gestureNavigationEnabled: true,
- ),
- );
-
- final MyWebViewPlatform builder = WebView.platform as MyWebViewPlatform;
- final MyWebViewPlatformController platform = builder.lastPlatformBuilt!;
-
- expect(
- platform.creationParams,
- MatchesCreationParams(CreationParams(
- initialUrl: 'https://youtube.com',
- webSettings: WebSettings(
- javascriptMode: JavascriptMode.disabled,
- hasNavigationDelegate: false,
- debuggingEnabled: false,
- userAgent: const WebSetting<String?>.of(null),
- gestureNavigationEnabled: true,
- zoomEnabled: true,
- ),
- )));
- });
-
- testWidgets('loadUrl', (WidgetTester tester) async {
- late WebViewController controller;
- await tester.pumpWidget(
- WebView(
- initialUrl: 'https://youtube.com',
- onWebViewCreated: (WebViewController webViewController) {
- controller = webViewController;
- },
- ),
- );
-
- final MyWebViewPlatform builder = WebView.platform as MyWebViewPlatform;
- final MyWebViewPlatformController platform = builder.lastPlatformBuilt!;
-
- final Map<String, String> headers = <String, String>{
- 'header': 'value',
- };
-
- await controller.loadUrl('https://google.com', headers: headers);
-
- expect(platform.lastUrlLoaded, 'https://google.com');
- expect(platform.lastRequestHeaders, headers);
- });
- });
-
- testWidgets('Set UserAgent', (WidgetTester tester) async {
- await tester.pumpWidget(const WebView(
- initialUrl: 'https://youtube.com',
- javascriptMode: JavascriptMode.unrestricted,
- ));
-
- final CreationParams params = captureBuildArgs(
- mockWebViewPlatform,
- creationParams: true,
- ).single as CreationParams;
-
- expect(params.webSettings!.userAgent.value, isNull);
-
- await tester.pumpWidget(const WebView(
- initialUrl: 'https://youtube.com',
- javascriptMode: JavascriptMode.unrestricted,
- userAgent: 'UA',
- ));
-
- final WebSettings settings =
- verify(mockWebViewPlatformController.updateSettings(captureAny))
- .captured
- .last as WebSettings;
- expect(settings.userAgent.value, 'UA');
- });
-}
-
-List<dynamic> captureBuildArgs(
- MockWebViewPlatform mockWebViewPlatform, {
- bool context = false,
- bool creationParams = false,
- bool webViewPlatformCallbacksHandler = false,
- bool javascriptChannelRegistry = false,
- bool onWebViewPlatformCreated = false,
- bool gestureRecognizers = false,
-}) {
- return verify(mockWebViewPlatform.build(
- context: context ? captureAnyNamed('context') : anyNamed('context'),
- creationParams: creationParams
- ? captureAnyNamed('creationParams')
- : anyNamed('creationParams'),
- webViewPlatformCallbacksHandler: webViewPlatformCallbacksHandler
- ? captureAnyNamed('webViewPlatformCallbacksHandler')
- : anyNamed('webViewPlatformCallbacksHandler'),
- javascriptChannelRegistry: javascriptChannelRegistry
- ? captureAnyNamed('javascriptChannelRegistry')
- : anyNamed('javascriptChannelRegistry'),
- onWebViewPlatformCreated: onWebViewPlatformCreated
- ? captureAnyNamed('onWebViewPlatformCreated')
- : anyNamed('onWebViewPlatformCreated'),
- gestureRecognizers: gestureRecognizers
- ? captureAnyNamed('gestureRecognizers')
- : anyNamed('gestureRecognizers'),
- )).captured;
-}
-
-// This Widget ensures that onWebViewPlatformCreated is only called once when
-// making multiple calls to `WidgetTester.pumpWidget` with different parameters
-// for the WebView.
-class TestPlatformWebView extends StatefulWidget {
- const TestPlatformWebView({
- Key? key,
- required this.mockWebViewPlatformController,
- this.onWebViewPlatformCreated,
- }) : super(key: key);
-
- final MockWebViewPlatformController mockWebViewPlatformController;
- final WebViewPlatformCreatedCallback? onWebViewPlatformCreated;
-
- @override
- State<StatefulWidget> createState() => TestPlatformWebViewState();
-}
-
-class TestPlatformWebViewState extends State<TestPlatformWebView> {
- @override
- void initState() {
- super.initState();
- final WebViewPlatformCreatedCallback? onWebViewPlatformCreated =
- widget.onWebViewPlatformCreated;
- if (onWebViewPlatformCreated != null) {
- onWebViewPlatformCreated(widget.mockWebViewPlatformController);
- }
- }
-
- @override
- Widget build(BuildContext context) {
- return Container();
- }
-}
-
-class MyWebViewPlatform implements WebViewPlatform {
- MyWebViewPlatformController? lastPlatformBuilt;
-
- @override
- Widget build({
- BuildContext? context,
- CreationParams? creationParams,
- required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
- required JavascriptChannelRegistry javascriptChannelRegistry,
- WebViewPlatformCreatedCallback? onWebViewPlatformCreated,
- Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
- }) {
- assert(onWebViewPlatformCreated != null);
- lastPlatformBuilt = MyWebViewPlatformController(
- creationParams, gestureRecognizers, webViewPlatformCallbacksHandler);
- onWebViewPlatformCreated!(lastPlatformBuilt);
- return Container();
- }
-
- @override
- Future<bool> clearCookies() {
- return Future<bool>.sync(() => true);
- }
-}
-
-class MyWebViewPlatformController extends WebViewPlatformController {
- MyWebViewPlatformController(this.creationParams, this.gestureRecognizers,
- WebViewPlatformCallbacksHandler platformHandler)
- : super(platformHandler);
-
- CreationParams? creationParams;
- Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers;
-
- String? lastUrlLoaded;
- Map<String, String>? lastRequestHeaders;
-
- @override
- Future<void> loadUrl(String url, Map<String, String>? headers) async {
- equals(1, 1);
- lastUrlLoaded = url;
- lastRequestHeaders = headers;
- }
-}
-
-class MatchesWebSettings extends Matcher {
- MatchesWebSettings(this._webSettings);
-
- final WebSettings? _webSettings;
-
- @override
- Description describe(Description description) =>
- description.add('$_webSettings');
-
- @override
- bool matches(
- covariant WebSettings webSettings, Map<dynamic, dynamic> matchState) {
- return _webSettings!.javascriptMode == webSettings.javascriptMode &&
- _webSettings!.hasNavigationDelegate ==
- webSettings.hasNavigationDelegate &&
- _webSettings!.debuggingEnabled == webSettings.debuggingEnabled &&
- _webSettings!.gestureNavigationEnabled ==
- webSettings.gestureNavigationEnabled &&
- _webSettings!.userAgent == webSettings.userAgent &&
- _webSettings!.zoomEnabled == webSettings.zoomEnabled;
- }
-}
-
-class MatchesCreationParams extends Matcher {
- MatchesCreationParams(this._creationParams);
-
- final CreationParams _creationParams;
-
- @override
- Description describe(Description description) =>
- description.add('$_creationParams');
-
- @override
- bool matches(covariant CreationParams creationParams,
- Map<dynamic, dynamic> matchState) {
- return _creationParams.initialUrl == creationParams.initialUrl &&
- MatchesWebSettings(_creationParams.webSettings)
- .matches(creationParams.webSettings!, matchState) &&
- orderedEquals(_creationParams.javascriptChannelNames)
- .matches(creationParams.javascriptChannelNames, matchState);
- }
-}
-
-class MockWebViewCookieManagerPlatform extends WebViewCookieManagerPlatform {
- List<WebViewCookie> setCookieCalls = <WebViewCookie>[];
-
- @override
- Future<bool> clearCookies() async => true;
-
- @override
- Future<void> setCookie(WebViewCookie cookie) async {
- setCookieCalls.add(cookie);
- }
-
- void reset() {
- setCookieCalls = <WebViewCookie>[];
- }
}
diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart
deleted file mode 100644
index a7a2100..0000000
--- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart
+++ /dev/null
@@ -1,213 +0,0 @@
-// Mocks generated by Mockito 5.3.0 from annotations
-// in webview_flutter/test/webview_flutter_test.dart.
-// Do not manually edit this file.
-
-// ignore_for_file: no_leading_underscores_for_library_prefixes
-import 'dart:async' as _i9;
-
-import 'package:flutter/foundation.dart' as _i3;
-import 'package:flutter/gestures.dart' as _i8;
-import 'package:flutter/widgets.dart' as _i2;
-import 'package:mockito/mockito.dart' as _i1;
-import 'package:webview_flutter_platform_interface/src/platform_interface/javascript_channel_registry.dart'
- as _i7;
-import 'package:webview_flutter_platform_interface/src/platform_interface/webview_platform.dart'
- as _i4;
-import 'package:webview_flutter_platform_interface/src/platform_interface/webview_platform_callbacks_handler.dart'
- as _i6;
-import 'package:webview_flutter_platform_interface/src/platform_interface/webview_platform_controller.dart'
- as _i10;
-import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i5;
-
-// ignore_for_file: type=lint
-// ignore_for_file: avoid_redundant_argument_values
-// ignore_for_file: avoid_setters_without_getters
-// ignore_for_file: comment_references
-// ignore_for_file: implementation_imports
-// ignore_for_file: invalid_use_of_visible_for_testing_member
-// ignore_for_file: prefer_const_constructors
-// ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: camel_case_types
-// ignore_for_file: subtype_of_sealed_class
-
-class _FakeWidget_0 extends _i1.SmartFake implements _i2.Widget {
- _FakeWidget_0(Object parent, Invocation parentInvocation)
- : super(parent, parentInvocation);
-
- @override
- String toString({_i3.DiagnosticLevel? minLevel = _i3.DiagnosticLevel.info}) =>
- super.toString();
-}
-
-/// A class which mocks [WebViewPlatform].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockWebViewPlatform extends _i1.Mock implements _i4.WebViewPlatform {
- MockWebViewPlatform() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i2.Widget build(
- {_i2.BuildContext? context,
- _i5.CreationParams? creationParams,
- _i6.WebViewPlatformCallbacksHandler? webViewPlatformCallbacksHandler,
- _i7.JavascriptChannelRegistry? javascriptChannelRegistry,
- _i4.WebViewPlatformCreatedCallback? onWebViewPlatformCreated,
- Set<_i3.Factory<_i8.OneSequenceGestureRecognizer>>?
- gestureRecognizers}) =>
- (super.noSuchMethod(
- Invocation.method(#build, [], {
- #context: context,
- #creationParams: creationParams,
- #webViewPlatformCallbacksHandler: webViewPlatformCallbacksHandler,
- #javascriptChannelRegistry: javascriptChannelRegistry,
- #onWebViewPlatformCreated: onWebViewPlatformCreated,
- #gestureRecognizers: gestureRecognizers
- }),
- returnValue: _FakeWidget_0(
- this,
- Invocation.method(#build, [], {
- #context: context,
- #creationParams: creationParams,
- #webViewPlatformCallbacksHandler:
- webViewPlatformCallbacksHandler,
- #javascriptChannelRegistry: javascriptChannelRegistry,
- #onWebViewPlatformCreated: onWebViewPlatformCreated,
- #gestureRecognizers: gestureRecognizers
- }))) as _i2.Widget);
- @override
- _i9.Future<bool> clearCookies() =>
- (super.noSuchMethod(Invocation.method(#clearCookies, []),
- returnValue: _i9.Future<bool>.value(false)) as _i9.Future<bool>);
-}
-
-/// A class which mocks [WebViewPlatformController].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockWebViewPlatformController extends _i1.Mock
- implements _i10.WebViewPlatformController {
- MockWebViewPlatformController() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i9.Future<void> loadFile(String? absoluteFilePath) => (super.noSuchMethod(
- Invocation.method(#loadFile, [absoluteFilePath]),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub: _i9.Future<void>.value()) as _i9.Future<void>);
- @override
- _i9.Future<void> loadFlutterAsset(String? key) => (super.noSuchMethod(
- Invocation.method(#loadFlutterAsset, [key]),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub: _i9.Future<void>.value()) as _i9.Future<void>);
- @override
- _i9.Future<void> loadHtmlString(String? html, {String? baseUrl}) =>
- (super.noSuchMethod(
- Invocation.method(#loadHtmlString, [html], {#baseUrl: baseUrl}),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub: _i9.Future<void>.value())
- as _i9.Future<void>);
- @override
- _i9.Future<void> loadUrl(String? url, Map<String, String>? headers) =>
- (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub: _i9.Future<void>.value())
- as _i9.Future<void>);
- @override
- _i9.Future<void> loadRequest(_i5.WebViewRequest? request) =>
- (super.noSuchMethod(Invocation.method(#loadRequest, [request]),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub: _i9.Future<void>.value())
- as _i9.Future<void>);
- @override
- _i9.Future<void> updateSettings(_i5.WebSettings? setting) =>
- (super.noSuchMethod(Invocation.method(#updateSettings, [setting]),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub: _i9.Future<void>.value())
- as _i9.Future<void>);
- @override
- _i9.Future<String?> currentUrl() =>
- (super.noSuchMethod(Invocation.method(#currentUrl, []),
- returnValue: _i9.Future<String?>.value()) as _i9.Future<String?>);
- @override
- _i9.Future<bool> canGoBack() =>
- (super.noSuchMethod(Invocation.method(#canGoBack, []),
- returnValue: _i9.Future<bool>.value(false)) as _i9.Future<bool>);
- @override
- _i9.Future<bool> canGoForward() =>
- (super.noSuchMethod(Invocation.method(#canGoForward, []),
- returnValue: _i9.Future<bool>.value(false)) as _i9.Future<bool>);
- @override
- _i9.Future<void> goBack() => (super.noSuchMethod(
- Invocation.method(#goBack, []),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub: _i9.Future<void>.value()) as _i9.Future<void>);
- @override
- _i9.Future<void> goForward() => (super.noSuchMethod(
- Invocation.method(#goForward, []),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub: _i9.Future<void>.value()) as _i9.Future<void>);
- @override
- _i9.Future<void> reload() => (super.noSuchMethod(
- Invocation.method(#reload, []),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub: _i9.Future<void>.value()) as _i9.Future<void>);
- @override
- _i9.Future<void> clearCache() => (super.noSuchMethod(
- Invocation.method(#clearCache, []),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub: _i9.Future<void>.value()) as _i9.Future<void>);
- @override
- _i9.Future<String> evaluateJavascript(String? javascript) =>
- (super.noSuchMethod(Invocation.method(#evaluateJavascript, [javascript]),
- returnValue: _i9.Future<String>.value('')) as _i9.Future<String>);
- @override
- _i9.Future<void> runJavascript(String? javascript) => (super.noSuchMethod(
- Invocation.method(#runJavascript, [javascript]),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub: _i9.Future<void>.value()) as _i9.Future<void>);
- @override
- _i9.Future<String> runJavascriptReturningResult(String? javascript) =>
- (super.noSuchMethod(
- Invocation.method(#runJavascriptReturningResult, [javascript]),
- returnValue: _i9.Future<String>.value('')) as _i9.Future<String>);
- @override
- _i9.Future<void> addJavascriptChannels(Set<String>? javascriptChannelNames) =>
- (super.noSuchMethod(
- Invocation.method(#addJavascriptChannels, [javascriptChannelNames]),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub:
- _i9.Future<void>.value()) as _i9.Future<void>);
- @override
- _i9.Future<void> removeJavascriptChannels(
- Set<String>? javascriptChannelNames) =>
- (super.noSuchMethod(
- Invocation.method(
- #removeJavascriptChannels, [javascriptChannelNames]),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub: _i9.Future<void>.value())
- as _i9.Future<void>);
- @override
- _i9.Future<String?> getTitle() =>
- (super.noSuchMethod(Invocation.method(#getTitle, []),
- returnValue: _i9.Future<String?>.value()) as _i9.Future<String?>);
- @override
- _i9.Future<void> scrollTo(int? x, int? y) => (super.noSuchMethod(
- Invocation.method(#scrollTo, [x, y]),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub: _i9.Future<void>.value()) as _i9.Future<void>);
- @override
- _i9.Future<void> scrollBy(int? x, int? y) => (super.noSuchMethod(
- Invocation.method(#scrollBy, [x, y]),
- returnValue: _i9.Future<void>.value(),
- returnValueForMissingStub: _i9.Future<void>.value()) as _i9.Future<void>);
- @override
- _i9.Future<int> getScrollX() =>
- (super.noSuchMethod(Invocation.method(#getScrollX, []),
- returnValue: _i9.Future<int>.value(0)) as _i9.Future<int>);
- @override
- _i9.Future<int> getScrollY() =>
- (super.noSuchMethod(Invocation.method(#getScrollY, []),
- returnValue: _i9.Future<int>.value(0)) as _i9.Future<int>);
-}
diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.dart b/packages/webview_flutter/webview_flutter/test/webview_widget_test.dart
similarity index 90%
rename from packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.dart
rename to packages/webview_flutter/webview_flutter/test/webview_widget_test.dart
index 455d8b3..21e9f53 100644
--- a/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.dart
+++ b/packages/webview_flutter/webview_flutter/test/webview_widget_test.dart
@@ -8,8 +8,8 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
-import 'package:webview_flutter/src/v4/webview_flutter.dart';
-import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter/webview_flutter.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
import 'webview_widget_test.mocks.dart';
@@ -77,8 +77,7 @@
}
class TestPlatformWebViewWidget extends PlatformWebViewWidget {
- TestPlatformWebViewWidget(PlatformWebViewWidgetCreationParams params)
- : super.implementation(params);
+ TestPlatformWebViewWidget(super.params) : super.implementation();
@override
Widget build(BuildContext context) {
diff --git a/packages/webview_flutter/webview_flutter/test/webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/webview_widget_test.mocks.dart
new file mode 100644
index 0000000..0e29ede
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter/test/webview_widget_test.mocks.dart
@@ -0,0 +1,396 @@
+// Mocks generated by Mockito 5.3.2 from annotations
+// in webview_flutter/test/webview_widget_test.dart.
+// Do not manually edit this file.
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:async' as _i7;
+import 'dart:ui' as _i3;
+
+import 'package:flutter/foundation.dart' as _i5;
+import 'package:flutter/widgets.dart' as _i4;
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:webview_flutter_platform_interface/src/platform_navigation_delegate.dart'
+ as _i8;
+import 'package:webview_flutter_platform_interface/src/platform_webview_controller.dart'
+ as _i6;
+import 'package:webview_flutter_platform_interface/src/platform_webview_widget.dart'
+ as _i9;
+import 'package:webview_flutter_platform_interface/src/webview_platform.dart'
+ as _i2;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+class _FakePlatformWebViewControllerCreationParams_0 extends _i1.SmartFake
+ implements _i2.PlatformWebViewControllerCreationParams {
+ _FakePlatformWebViewControllerCreationParams_0(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakeObject_1 extends _i1.SmartFake implements Object {
+ _FakeObject_1(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakeOffset_2 extends _i1.SmartFake implements _i3.Offset {
+ _FakeOffset_2(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakePlatformWebViewWidgetCreationParams_3 extends _i1.SmartFake
+ implements _i2.PlatformWebViewWidgetCreationParams {
+ _FakePlatformWebViewWidgetCreationParams_3(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakeWidget_4 extends _i1.SmartFake implements _i4.Widget {
+ _FakeWidget_4(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+
+ @override
+ String toString({_i5.DiagnosticLevel? minLevel = _i5.DiagnosticLevel.info}) =>
+ super.toString();
+}
+
+/// A class which mocks [PlatformWebViewController].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockPlatformWebViewController extends _i1.Mock
+ implements _i6.PlatformWebViewController {
+ MockPlatformWebViewController() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i2.PlatformWebViewControllerCreationParams get params => (super.noSuchMethod(
+ Invocation.getter(#params),
+ returnValue: _FakePlatformWebViewControllerCreationParams_0(
+ this,
+ Invocation.getter(#params),
+ ),
+ ) as _i2.PlatformWebViewControllerCreationParams);
+ @override
+ _i7.Future<void> loadFile(String? absoluteFilePath) => (super.noSuchMethod(
+ Invocation.method(
+ #loadFile,
+ [absoluteFilePath],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<void> loadFlutterAsset(String? key) => (super.noSuchMethod(
+ Invocation.method(
+ #loadFlutterAsset,
+ [key],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<void> loadHtmlString(
+ String? html, {
+ String? baseUrl,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #loadHtmlString,
+ [html],
+ {#baseUrl: baseUrl},
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<void> loadRequest(_i2.LoadRequestParams? params) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #loadRequest,
+ [params],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<String?> currentUrl() => (super.noSuchMethod(
+ Invocation.method(
+ #currentUrl,
+ [],
+ ),
+ returnValue: _i7.Future<String?>.value(),
+ ) as _i7.Future<String?>);
+ @override
+ _i7.Future<bool> canGoBack() => (super.noSuchMethod(
+ Invocation.method(
+ #canGoBack,
+ [],
+ ),
+ returnValue: _i7.Future<bool>.value(false),
+ ) as _i7.Future<bool>);
+ @override
+ _i7.Future<bool> canGoForward() => (super.noSuchMethod(
+ Invocation.method(
+ #canGoForward,
+ [],
+ ),
+ returnValue: _i7.Future<bool>.value(false),
+ ) as _i7.Future<bool>);
+ @override
+ _i7.Future<void> goBack() => (super.noSuchMethod(
+ Invocation.method(
+ #goBack,
+ [],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<void> goForward() => (super.noSuchMethod(
+ Invocation.method(
+ #goForward,
+ [],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<void> reload() => (super.noSuchMethod(
+ Invocation.method(
+ #reload,
+ [],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<void> clearCache() => (super.noSuchMethod(
+ Invocation.method(
+ #clearCache,
+ [],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<void> clearLocalStorage() => (super.noSuchMethod(
+ Invocation.method(
+ #clearLocalStorage,
+ [],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<void> setPlatformNavigationDelegate(
+ _i8.PlatformNavigationDelegate? handler) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setPlatformNavigationDelegate,
+ [handler],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<void> runJavaScript(String? javaScript) => (super.noSuchMethod(
+ Invocation.method(
+ #runJavaScript,
+ [javaScript],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<Object> runJavaScriptReturningResult(String? javaScript) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #runJavaScriptReturningResult,
+ [javaScript],
+ ),
+ returnValue: _i7.Future<Object>.value(_FakeObject_1(
+ this,
+ Invocation.method(
+ #runJavaScriptReturningResult,
+ [javaScript],
+ ),
+ )),
+ ) as _i7.Future<Object>);
+ @override
+ _i7.Future<void> addJavaScriptChannel(
+ _i6.JavaScriptChannelParams? javaScriptChannelParams) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #addJavaScriptChannel,
+ [javaScriptChannelParams],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<void> removeJavaScriptChannel(String? javaScriptChannelName) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #removeJavaScriptChannel,
+ [javaScriptChannelName],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<String?> getTitle() => (super.noSuchMethod(
+ Invocation.method(
+ #getTitle,
+ [],
+ ),
+ returnValue: _i7.Future<String?>.value(),
+ ) as _i7.Future<String?>);
+ @override
+ _i7.Future<void> scrollTo(
+ int? x,
+ int? y,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #scrollTo,
+ [
+ x,
+ y,
+ ],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<void> scrollBy(
+ int? x,
+ int? y,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #scrollBy,
+ [
+ x,
+ y,
+ ],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<_i3.Offset> getScrollPosition() => (super.noSuchMethod(
+ Invocation.method(
+ #getScrollPosition,
+ [],
+ ),
+ returnValue: _i7.Future<_i3.Offset>.value(_FakeOffset_2(
+ this,
+ Invocation.method(
+ #getScrollPosition,
+ [],
+ ),
+ )),
+ ) as _i7.Future<_i3.Offset>);
+ @override
+ _i7.Future<void> enableZoom(bool? enabled) => (super.noSuchMethod(
+ Invocation.method(
+ #enableZoom,
+ [enabled],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<void> setBackgroundColor(_i3.Color? color) => (super.noSuchMethod(
+ Invocation.method(
+ #setBackgroundColor,
+ [color],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<void> setJavaScriptMode(_i2.JavaScriptMode? javaScriptMode) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setJavaScriptMode,
+ [javaScriptMode],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+ @override
+ _i7.Future<void> setUserAgent(String? userAgent) => (super.noSuchMethod(
+ Invocation.method(
+ #setUserAgent,
+ [userAgent],
+ ),
+ returnValue: _i7.Future<void>.value(),
+ returnValueForMissingStub: _i7.Future<void>.value(),
+ ) as _i7.Future<void>);
+}
+
+/// A class which mocks [PlatformWebViewWidget].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockPlatformWebViewWidget extends _i1.Mock
+ implements _i9.PlatformWebViewWidget {
+ MockPlatformWebViewWidget() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i2.PlatformWebViewWidgetCreationParams get params => (super.noSuchMethod(
+ Invocation.getter(#params),
+ returnValue: _FakePlatformWebViewWidgetCreationParams_3(
+ this,
+ Invocation.getter(#params),
+ ),
+ ) as _i2.PlatformWebViewWidgetCreationParams);
+ @override
+ _i4.Widget build(_i4.BuildContext? context) => (super.noSuchMethod(
+ Invocation.method(
+ #build,
+ [context],
+ ),
+ returnValue: _FakeWidget_4(
+ this,
+ Invocation.method(
+ #build,
+ [context],
+ ),
+ ),
+ ) as _i4.Widget);
+}
diff --git a/script/configs/exclude_all_plugins_app.yaml b/script/configs/exclude_all_plugins_app.yaml
index a19f5fe..8dd0fde 100644
--- a/script/configs/exclude_all_plugins_app.yaml
+++ b/script/configs/exclude_all_plugins_app.yaml
@@ -8,10 +8,3 @@
# This is a permament entry, as it should never be a direct app dependency.
- plugin_platform_interface
-# Packages below are temporarily added to push and release a new webview
-# interface. Remove packages with release of `webview_flutter` 4.0.0. See
-# https://github.com/flutter/flutter/issues/94051.
-- webview_flutter_platform_interface
-- webview_flutter_wkwebview
-- webview_flutter_android
-- webview_flutter_web
diff --git a/script/configs/temp_exclude_excerpt.yaml b/script/configs/temp_exclude_excerpt.yaml
index c59983e..4034629 100644
--- a/script/configs/temp_exclude_excerpt.yaml
+++ b/script/configs/temp_exclude_excerpt.yaml
@@ -18,6 +18,5 @@
- plugin_platform_interface
- quick_actions/quick_actions
- shared_preferences/shared_preferences
-- webview_flutter/webview_flutter
- webview_flutter_android
- webview_flutter_web