diff --git a/CHANGELOG.md b/CHANGELOG.md
index 87a3cb08..cf08cdeb 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@
- Added support for `onPermissionRequest` event on iOS 15.0+
- Updated `getMetaThemeColor` on iOS 15.0+
+### BREAKING CHANGES
+
+- On Android, the `InAppWebView` widget uses hybrid composition by default (`useHybridComposition: true`).
+
## 5.4.3
- Added Bitwise OR operator support for `AndroidActionModeMenuItem` class
diff --git a/example/integration_test/constants.dart b/example/integration_test/constants.dart
index 781bc2ee..d774a815 100644
--- a/example/integration_test/constants.dart
+++ b/example/integration_test/constants.dart
@@ -3,4 +3,7 @@ final TEST_CROSS_PLATFORM_URL_1 = Uri.parse('https://flutter.dev');
final TEST_CROSS_PLATFORM_URL_2 = Uri.parse('https://www.bing.com/');
final TEST_URL_1 = Uri.parse('https://github.com/flutter');
final TEST_URL_2 = Uri.parse('https://www.google.com/');
-final TEST_WEB_PLATFORM_URL_1 = Uri.parse(Uri.base.toString().replaceFirst("/#/", "/") + 'page.html');
\ No newline at end of file
+final TEST_URL_3 = Uri.parse('https://github.com/pichillilorenzo/flutter_inappwebview');
+final TEST_URL_4 = Uri.parse('https://www.youtube.com/');
+final TEST_WEB_PLATFORM_URL_1 = Uri.parse(Uri.base.toString().replaceFirst("/#/", "/") + 'page.html');
+final TEST_NOT_A_WEBSITE_URL = Uri.parse('https://www.notawebsite..com');
\ No newline at end of file
diff --git a/example/integration_test/in_app_webview/audio_playback_policy.dart b/example/integration_test/in_app_webview/audio_playback_policy.dart
new file mode 100644
index 00000000..0112c8da
--- /dev/null
+++ b/example/integration_test/in_app_webview/audio_playback_policy.dart
@@ -0,0 +1,125 @@
+import 'dart:async';
+import 'dart:convert';
+import 'dart:typed_data';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_inappwebview/flutter_inappwebview.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../constants.dart';
+
+void audioPlaybackPolicy() {
+ final shouldSkip = kIsWeb || ![
+ TargetPlatform.android,
+ TargetPlatform.iOS,
+ TargetPlatform.macOS,
+ ].contains(defaultTargetPlatform);
+
+ group('Audio playback policy', () {
+ String audioTestBase64 = "";
+ setUpAll(() async {
+ final ByteData audioData =
+ await rootBundle.load('test_assets/sample_audio.ogg');
+ final String base64AudioData =
+ base64Encode(Uint8List.view(audioData.buffer));
+ final String audioTest = '''
+
+
Audio auto play
+
+
+
+
+
+
+ ''';
+ audioTestBase64 = base64Encode(const Utf8Encoder().convert(audioTest));
+ });
+
+ testWidgets('Auto media playback', (WidgetTester tester) async {
+ Completer controllerCompleter =
+ Completer();
+ Completer pageStarted = Completer();
+ Completer pageLoaded = Completer();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: InAppWebView(
+ key: GlobalKey(),
+ initialUrlRequest: URLRequest(
+ url: Uri.parse(
+ 'data:text/html;charset=utf-8;base64,$audioTestBase64')),
+ onWebViewCreated: (controller) {
+ controllerCompleter.complete(controller);
+ },
+ initialSettings: InAppWebViewSettings(
+ javaScriptEnabled: true,
+ mediaPlaybackRequiresUserGesture: false),
+ onLoadStart: (controller, url) {
+ pageStarted.complete();
+ },
+ onLoadStop: (controller, url) {
+ pageLoaded.complete();
+ },
+ ),
+ ),
+ );
+ InAppWebViewController controller = await controllerCompleter.future;
+ await pageStarted.future;
+ await pageLoaded.future;
+
+ bool isPaused =
+ await controller.evaluateJavascript(source: 'isPaused();');
+ expect(isPaused, false);
+
+ controllerCompleter = Completer();
+ pageStarted = Completer();
+ pageLoaded = Completer();
+
+ // We change the key to re-create a new webview as we change the mediaPlaybackRequiresUserGesture
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: InAppWebView(
+ key: GlobalKey(),
+ initialUrlRequest: URLRequest(
+ url: Uri.parse(
+ 'data:text/html;charset=utf-8;base64,$audioTestBase64')),
+ onWebViewCreated: (controller) {
+ controllerCompleter.complete(controller);
+ },
+ initialSettings: InAppWebViewSettings(
+ javaScriptEnabled: true,
+ mediaPlaybackRequiresUserGesture: true),
+ onLoadStart: (controller, url) {
+ pageStarted.complete();
+ },
+ onLoadStop: (controller, url) {
+ pageLoaded.complete();
+ },
+ ),
+ ),
+ );
+
+ controller = await controllerCompleter.future;
+ await pageStarted.future;
+ await pageLoaded.future;
+
+ isPaused = await controller.evaluateJavascript(source: 'isPaused();');
+ expect(isPaused, true);
+ });
+ }, skip: shouldSkip);
+}
\ No newline at end of file
diff --git a/example/integration_test/in_app_webview/get_title.dart b/example/integration_test/in_app_webview/get_title.dart
new file mode 100644
index 00000000..da3e03ca
--- /dev/null
+++ b/example/integration_test/in_app_webview/get_title.dart
@@ -0,0 +1,67 @@
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_inappwebview/flutter_inappwebview.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../constants.dart';
+
+void getTitle() {
+ final shouldSkip = !kIsWeb || ![
+ TargetPlatform.android,
+ TargetPlatform.iOS,
+ TargetPlatform.macOS,
+ ].contains(defaultTargetPlatform);
+
+ final String getTitleTest = '''
+
+ Some title
+
+
+
+
+ ''';
+ final String getTitleTestBase64 =
+ base64Encode(const Utf8Encoder().convert(getTitleTest));
+
+ var url = !kIsWeb ? Uri.parse(
+ 'data:text/html;charset=utf-8;base64,$getTitleTestBase64') :
+ TEST_WEB_PLATFORM_URL_1;
+ var expectedValue = !kIsWeb ? 'Some title' : 'page';
+
+ testWidgets('getTitle', (WidgetTester tester) async {
+
+ final Completer pageStarted = Completer();
+ final Completer pageLoaded = Completer();
+ final Completer controllerCompleter = Completer();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: InAppWebView(
+ initialUrlRequest: URLRequest(
+ url: url),
+ onWebViewCreated: (controller) {
+ controllerCompleter.complete(controller);
+ },
+ onLoadStart: (controller, url) {
+ pageStarted.complete();
+ },
+ onLoadStop: (controller, url) {
+ pageLoaded.complete();
+ },
+ ),
+ ),
+ );
+
+ final InAppWebViewController controller =
+ await controllerCompleter.future;
+ await pageStarted.future;
+ await pageLoaded.future;
+
+ final String? title = await controller.getTitle();
+ expect(title, expectedValue);
+ }, skip: shouldSkip);
+}
\ No newline at end of file
diff --git a/example/integration_test/in_app_webview/javascript_handler.dart b/example/integration_test/in_app_webview/javascript_handler.dart
new file mode 100644
index 00000000..0380701f
--- /dev/null
+++ b/example/integration_test/in_app_webview/javascript_handler.dart
@@ -0,0 +1,80 @@
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_inappwebview/flutter_inappwebview.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../util.dart';
+
+void javascriptHandler() {
+ final shouldSkip = kIsWeb || ![
+ TargetPlatform.android,
+ TargetPlatform.iOS,
+ TargetPlatform.macOS,
+ ].contains(defaultTargetPlatform);
+
+ testWidgets('JavaScript Handler', (WidgetTester tester) async {
+ final Completer controllerCompleter = Completer();
+ final Completer pageStarted = Completer();
+ final Completer pageLoaded = Completer();
+ final Completer handlerFoo = Completer();
+ final Completer handlerFooWithArgs = Completer();
+ final List messagesReceived = [];
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: InAppWebView(
+ key: GlobalKey(),
+ initialFile:
+ "test_assets/in_app_webview_javascript_handler_test.html",
+ onWebViewCreated: (controller) {
+ controllerCompleter.complete(controller);
+
+ controller.addJavaScriptHandler(
+ handlerName: 'handlerFoo',
+ callback: (args) {
+ handlerFoo.complete();
+ return Foo(bar: 'bar_value', baz: 'baz_value');
+ });
+
+ controller.addJavaScriptHandler(
+ handlerName: 'handlerFooWithArgs',
+ callback: (args) {
+ messagesReceived.add(args[0] as int);
+ messagesReceived.add(args[1] as bool);
+ messagesReceived.add(args[2] as List?);
+ messagesReceived.add(args[3]?.cast()
+ as Map?);
+ messagesReceived.add(args[4]?.cast()
+ as Map?);
+ handlerFooWithArgs.complete();
+ });
+ },
+ initialSettings: InAppWebViewSettings(javaScriptEnabled: true),
+ onLoadStart: (controller, url) {
+ pageStarted.complete();
+ },
+ onLoadStop: (controller, url) {
+ pageLoaded.complete();
+ },
+ ),
+ ),
+ );
+
+ await pageStarted.future;
+ await pageLoaded.future;
+ await handlerFoo.future;
+ await handlerFooWithArgs.future;
+
+ expect(messagesReceived[0], 1);
+ expect(messagesReceived[1], true);
+ expect(
+ listEquals(messagesReceived[2] as List?, ["bar", 5]), true);
+ expect(mapEquals(messagesReceived[3], {"foo": "baz"}), true);
+ expect(
+ mapEquals(
+ messagesReceived[4], {"bar": "bar_value", "baz": "baz_value"}),
+ true);
+ }, skip: shouldSkip);
+}
\ No newline at end of file
diff --git a/example/integration_test/in_app_webview/load_file_url.dart b/example/integration_test/in_app_webview/load_file_url.dart
new file mode 100644
index 00000000..aadf088f
--- /dev/null
+++ b/example/integration_test/in_app_webview/load_file_url.dart
@@ -0,0 +1,150 @@
+import 'dart:async';
+import 'dart:io';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_inappwebview/flutter_inappwebview.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:path_provider/path_provider.dart';
+
+void loadFileUrl() {
+ final shouldSkip = kIsWeb ||
+ ![
+ TargetPlatform.iOS,
+ TargetPlatform.macOS,
+ ].contains(defaultTargetPlatform);
+
+ group('load file URL', () {
+ late Directory appSupportDir;
+ late File fileHtml;
+ late File fileJs;
+
+ setUpAll(() async {
+ appSupportDir = (await getApplicationSupportDirectory());
+
+ final Directory htmlFolder = Directory('${appSupportDir.path}/html/');
+ if (!await htmlFolder.exists()) {
+ await htmlFolder.create(recursive: true);
+ }
+
+ final Directory jsFolder = Directory('${appSupportDir.path}/js/');
+ if (!await jsFolder.exists()) {
+ await jsFolder.create(recursive: true);
+ }
+
+ var html = """
+
+
+ file scheme
+
+
+
+
+
+ """;
+ fileHtml = File(htmlFolder.path + "index.html");
+ fileHtml.writeAsStringSync(html);
+
+ var js = """
+ console.log('message');
+ """;
+ fileJs = File(jsFolder.path + "main.js");
+ fileJs.writeAsStringSync(js);
+ });
+
+ testWidgets('initialUrl with file:// scheme and allowingReadAccessTo',
+ (WidgetTester tester) async {
+ final Completer consoleMessageShouldNotComplete =
+ Completer();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: InAppWebView(
+ key: GlobalKey(),
+ initialUrlRequest:
+ URLRequest(url: Uri.parse('file://${fileHtml.path}')),
+ onConsoleMessage: (controller, consoleMessage) {
+ consoleMessageShouldNotComplete.complete(consoleMessage);
+ },
+ ),
+ ),
+ );
+ var result = await consoleMessageShouldNotComplete.future
+ .timeout(const Duration(seconds: 2), onTimeout: () => null);
+ expect(result, null);
+
+ final Completer consoleMessageCompleter =
+ Completer();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: InAppWebView(
+ key: GlobalKey(),
+ initialUrlRequest:
+ URLRequest(url: Uri.parse('file://${fileHtml.path}')),
+ initialSettings: InAppWebViewSettings(
+ allowingReadAccessTo:
+ Uri.parse('file://${appSupportDir.path}/')),
+ onConsoleMessage: (controller, consoleMessage) {
+ consoleMessageCompleter.complete(consoleMessage);
+ },
+ ),
+ ),
+ );
+ final ConsoleMessage consoleMessage =
+ await consoleMessageCompleter.future;
+ expect(consoleMessage.messageLevel, ConsoleMessageLevel.LOG);
+ expect(consoleMessage.message, 'message');
+ });
+
+ testWidgets('loadUrl with file:// scheme and allowingReadAccessTo argument',
+ (WidgetTester tester) async {
+ final Completer consoleMessageShouldNotComplete =
+ Completer();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: InAppWebView(
+ key: GlobalKey(),
+ onWebViewCreated: (controller) {
+ controller.loadUrl(
+ urlRequest:
+ URLRequest(url: Uri.parse('file://${fileHtml.path}')));
+ },
+ onConsoleMessage: (controller, consoleMessage) {
+ consoleMessageShouldNotComplete.complete(consoleMessage);
+ },
+ ),
+ ),
+ );
+ var result = await consoleMessageShouldNotComplete.future
+ .timeout(const Duration(seconds: 2), onTimeout: () => null);
+ expect(result, null);
+
+ final Completer consoleMessageCompleter =
+ Completer();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: InAppWebView(
+ key: GlobalKey(),
+ onWebViewCreated: (controller) {
+ controller.loadUrl(
+ urlRequest:
+ URLRequest(url: Uri.parse('file://${fileHtml.path}')),
+ allowingReadAccessTo:
+ Uri.parse('file://${appSupportDir.path}/'));
+ },
+ onConsoleMessage: (controller, consoleMessage) {
+ consoleMessageCompleter.complete(consoleMessage);
+ },
+ ),
+ ),
+ );
+ final ConsoleMessage consoleMessage =
+ await consoleMessageCompleter.future;
+ expect(consoleMessage.messageLevel, ConsoleMessageLevel.LOG);
+ expect(consoleMessage.message, 'message');
+ });
+ }, skip: shouldSkip);
+}
diff --git a/example/integration_test/in_app_webview/main.dart b/example/integration_test/in_app_webview/main.dart
index fc3d2a6e..7d30a0ef 100644
--- a/example/integration_test/in_app_webview/main.dart
+++ b/example/integration_test/in_app_webview/main.dart
@@ -1,9 +1,19 @@
import 'package:flutter_test/flutter_test.dart';
+import 'audio_playback_policy.dart';
+import 'get_title.dart';
import 'initial_url_request.dart';
-import 'set_get_settings.dart';
import 'javascript_code_evaluation.dart';
+import 'javascript_handler.dart';
+import 'load_file_url.dart';
import 'load_url.dart';
+import 'on_load_error.dart';
+import 'programmatic_scroll.dart';
+import 'resize_webview.dart';
+import 'set_custom_useragent.dart';
+import 'set_get_settings.dart';
+import 'should_override_url_loading.dart';
+import 'video_playback_policy.dart';
void main() {
group('InAppWebView', () {
@@ -11,5 +21,15 @@ void main() {
setGetSettings();
javascriptCodeEvaluation();
loadUrl();
+ loadFileUrl();
+ javascriptHandler();
+ resizeWebView();
+ setCustomUserAgent();
+ videoPlaybackPolicy();
+ audioPlaybackPolicy();
+ getTitle();
+ programmaticScroll();
+ shouldOverrideUrlLoading();
+ onLoadError();
});
}
\ No newline at end of file
diff --git a/example/integration_test/in_app_webview/on_load_error.dart b/example/integration_test/in_app_webview/on_load_error.dart
new file mode 100644
index 00000000..5c83c419
--- /dev/null
+++ b/example/integration_test/in_app_webview/on_load_error.dart
@@ -0,0 +1,77 @@
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_inappwebview/flutter_inappwebview.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../constants.dart';
+
+void onLoadError() {
+ final shouldSkip = kIsWeb ||
+ ![
+ TargetPlatform.android,
+ TargetPlatform.iOS,
+ TargetPlatform.macOS,
+ ].contains(defaultTargetPlatform);
+
+ group('onLoadError', () {
+ testWidgets('invalid url', (WidgetTester tester) async {
+ final Completer errorUrlCompleter = Completer();
+ final Completer errorCodeCompleter = Completer();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: InAppWebView(
+ key: GlobalKey(),
+ initialUrlRequest: URLRequest(url: TEST_NOT_A_WEBSITE_URL),
+ onLoadError: (controller, url, code, message) {
+ errorUrlCompleter.complete(url.toString());
+ errorCodeCompleter.complete(code);
+ },
+ ),
+ ),
+ );
+
+ final String url = await errorUrlCompleter.future;
+ final int code = await errorCodeCompleter.future;
+
+ if (defaultTargetPlatform == TargetPlatform.android) {
+ expect(code, -2);
+ } else if (defaultTargetPlatform == TargetPlatform.iOS ||
+ defaultTargetPlatform == TargetPlatform.macOS) {
+ expect(code, -1003);
+ }
+ expect(url, TEST_NOT_A_WEBSITE_URL.toString());
+ });
+
+ testWidgets('event is not called with valid url',
+ (WidgetTester tester) async {
+ final Completer errorUrlCompleter = Completer();
+ final Completer errorCodeCompleter = Completer();
+ final Completer errorMessageCompleter = Completer();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: InAppWebView(
+ key: GlobalKey(),
+ initialUrlRequest: URLRequest(
+ url: Uri.parse(
+ 'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+')),
+ onLoadError: (controller, url, code, message) {
+ errorUrlCompleter.complete(url.toString());
+ errorCodeCompleter.complete(code);
+ errorMessageCompleter.complete(message);
+ },
+ ),
+ ),
+ );
+
+ expect(errorUrlCompleter.future, doesNotComplete);
+ expect(errorCodeCompleter.future, doesNotComplete);
+ expect(errorMessageCompleter.future, doesNotComplete);
+ });
+ }, skip: shouldSkip);
+}
diff --git a/example/integration_test/in_app_webview/programmatic_scroll.dart b/example/integration_test/in_app_webview/programmatic_scroll.dart
new file mode 100644
index 00000000..92cdb5ec
--- /dev/null
+++ b/example/integration_test/in_app_webview/programmatic_scroll.dart
@@ -0,0 +1,176 @@
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_inappwebview/flutter_inappwebview.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../constants.dart';
+
+void programmaticScroll() {
+ final shouldSkip = !kIsWeb || ![
+ TargetPlatform.android,
+ TargetPlatform.iOS,
+ TargetPlatform.macOS,
+ ].contains(defaultTargetPlatform);
+
+ group('Programmatic Scroll', () {
+ final shouldSkipTest1 = !kIsWeb || ![
+ TargetPlatform.android,
+ TargetPlatform.iOS,
+ TargetPlatform.macOS,
+ ].contains(defaultTargetPlatform);
+
+ testWidgets('set and get scroll position', (WidgetTester tester) async {
+ final String scrollTestPage = '''
+
+
+
+
+
+
+
+
+
+ ''';
+
+ final String scrollTestPageBase64 =
+ base64Encode(const Utf8Encoder().convert(scrollTestPage));
+
+ var url = !kIsWeb ? Uri.parse(
+ 'data:text/html;charset=utf-8;base64,$scrollTestPageBase64') :
+ TEST_WEB_PLATFORM_URL_1;
+
+ final Completer pageLoaded = Completer();
+ final Completer controllerCompleter =
+ Completer();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: InAppWebView(
+ initialUrlRequest: URLRequest(
+ url: url),
+ onWebViewCreated: (controller) {
+ controllerCompleter.complete(controller);
+ },
+ onLoadStop: (controller, url) {
+ pageLoaded.complete();
+ },
+ ),
+ ),
+ );
+
+ final InAppWebViewController controller =
+ await controllerCompleter.future;
+ await pageLoaded.future;
+ await controller.scrollTo(x: 0, y: 0);
+
+ await tester.pumpAndSettle(Duration(seconds: 3));
+
+ // Check scrollTo()
+ const int X_SCROLL = 123;
+ const int Y_SCROLL = 321;
+
+ await controller.scrollTo(x: X_SCROLL, y: Y_SCROLL);
+ await tester.pumpAndSettle(Duration(seconds: 2));
+ int? scrollPosX = await controller.getScrollX();
+ int? scrollPosY = await controller.getScrollY();
+ expect(scrollPosX, X_SCROLL);
+ expect(scrollPosY, Y_SCROLL);
+
+ // Check scrollBy() (on top of scrollTo())
+ await controller.scrollBy(x: X_SCROLL, y: Y_SCROLL);
+ await tester.pumpAndSettle(Duration(seconds: 2));
+ scrollPosX = await controller.getScrollX();
+ scrollPosY = await controller.getScrollY();
+ expect(scrollPosX, X_SCROLL * 2);
+ expect(scrollPosY, Y_SCROLL * 2);
+ }, skip: shouldSkipTest1);
+
+ testWidgets('set and get scroll position on Android without Hybrid Composition', (WidgetTester tester) async {
+ final String scrollTestPage = '''
+
+
+
+
+
+
+
+
+
+ ''';
+
+ final String scrollTestPageBase64 =
+ base64Encode(const Utf8Encoder().convert(scrollTestPage));
+
+ final Completer pageLoaded = Completer();
+ final Completer controllerCompleter =
+ Completer();
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: InAppWebView(
+ initialUrlRequest: URLRequest(
+ url: Uri.parse(
+ 'data:text/html;charset=utf-8;base64,$scrollTestPageBase64')),
+ onWebViewCreated: (controller) {
+ controllerCompleter.complete(controller);
+ },
+ initialSettings: InAppWebViewSettings(
+ useHybridComposition: false),
+ onLoadStop: (controller, url) {
+ pageLoaded.complete();
+ },
+ ),
+ ),
+ );
+
+ final InAppWebViewController controller =
+ await controllerCompleter.future;
+ await pageLoaded.future;
+ await controller.scrollTo(x: 0, y: 0);
+
+ await tester.pumpAndSettle(Duration(seconds: 3));
+
+ // Check scrollTo()
+ const int X_SCROLL = 123;
+ const int Y_SCROLL = 321;
+
+ await controller.scrollTo(x: X_SCROLL, y: Y_SCROLL);
+ await tester.pumpAndSettle(Duration(seconds: 2));
+ int? scrollPosX = await controller.getScrollX();
+ int? scrollPosY = await controller.getScrollY();
+ expect(scrollPosX, X_SCROLL);
+ expect(scrollPosY, Y_SCROLL);
+
+ // Check scrollBy() (on top of scrollTo())
+ await controller.scrollBy(x: X_SCROLL, y: Y_SCROLL);
+ await tester.pumpAndSettle(Duration(seconds: 2));
+ scrollPosX = await controller.getScrollX();
+ scrollPosY = await controller.getScrollY();
+ expect(scrollPosX, X_SCROLL * 2);
+ expect(scrollPosY, Y_SCROLL * 2);
+ }, skip: defaultTargetPlatform != TargetPlatform.android);
+ }, skip: shouldSkip);
+}
\ No newline at end of file
diff --git a/example/integration_test/in_app_webview/resize_webview.dart b/example/integration_test/in_app_webview/resize_webview.dart
new file mode 100644
index 00000000..43ff9084
--- /dev/null
+++ b/example/integration_test/in_app_webview/resize_webview.dart
@@ -0,0 +1,104 @@
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_inappwebview/flutter_inappwebview.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../constants.dart';
+
+void resizeWebView() {
+ final shouldSkip = kIsWeb || ![
+ TargetPlatform.android,
+ TargetPlatform.iOS,
+ TargetPlatform.macOS,
+ ].contains(defaultTargetPlatform);
+
+ testWidgets('resize webview', (WidgetTester tester) async {
+ final String resizeTest = '''
+
+ Resize test
+
+
+
+
+
+ ''';
+ final String resizeTestBase64 =
+ base64Encode(const Utf8Encoder().convert(resizeTest));
+ final Completer resizeCompleter = Completer();
+ final Completer pageStarted = Completer();
+ final Completer pageLoaded = Completer();
+ final Completer controllerCompleter = Completer();
+ final GlobalKey key = GlobalKey();
+
+ final InAppWebView webView = InAppWebView(
+ key: key,
+ initialUrlRequest: URLRequest(
+ url: Uri.parse(
+ 'data:text/html;charset=utf-8;base64,$resizeTestBase64')),
+ onWebViewCreated: (controller) {
+ controllerCompleter.complete(controller);
+
+ controller.addJavaScriptHandler(
+ handlerName: 'resize',
+ callback: (args) {
+ resizeCompleter.complete(true);
+ });
+ },
+ onLoadStart: (controller, url) {
+ pageStarted.complete();
+ },
+ onLoadStop: (controller, url) {
+ pageLoaded.complete();
+ },
+ initialSettings: InAppWebViewSettings(javaScriptEnabled: true),
+ );
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: Column(
+ children: [
+ SizedBox(
+ width: 200,
+ height: 200,
+ child: webView,
+ ),
+ ],
+ ),
+ ),
+ );
+
+ await controllerCompleter.future;
+ await pageStarted.future;
+ await pageLoaded.future;
+
+ expect(resizeCompleter.isCompleted, false);
+
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: Column(
+ children: [
+ SizedBox(
+ width: 400,
+ height: 400,
+ child: webView,
+ ),
+ ],
+ ),
+ ),
+ );
+
+ await resizeCompleter.future;
+ }, skip: shouldSkip);
+}
\ No newline at end of file
diff --git a/example/integration_test/in_app_webview/set_custom_useragent.dart b/example/integration_test/in_app_webview/set_custom_useragent.dart
new file mode 100644
index 00000000..cdf9d2b6
--- /dev/null
+++ b/example/integration_test/in_app_webview/set_custom_useragent.dart
@@ -0,0 +1,49 @@
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_inappwebview/flutter_inappwebview.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../constants.dart';
+
+void setCustomUserAgent() {
+ final shouldSkip = kIsWeb ||
+ ![
+ TargetPlatform.android,
+ TargetPlatform.iOS,
+ TargetPlatform.macOS,
+ ].contains(defaultTargetPlatform);
+
+ testWidgets('set custom userAgent', (WidgetTester tester) async {
+ final Completer controllerCompleter1 = Completer();
+ await tester.pumpWidget(
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: InAppWebView(
+ initialUrlRequest: URLRequest(url: TEST_URL_ABOUT_BLANK),
+ initialSettings: InAppWebViewSettings(
+ javaScriptEnabled: true,
+ userAgent: 'Custom_User_Agent1',
+ ),
+ onWebViewCreated: (controller) {
+ controllerCompleter1.complete(controller);
+ },
+ ),
+ ),
+ );
+ InAppWebViewController controller1 = await controllerCompleter1.future;
+ final String customUserAgent1 =
+ await controller1.evaluateJavascript(source: 'navigator.userAgent;');
+ expect(customUserAgent1, 'Custom_User_Agent1');
+
+ await controller1.setSettings(
+ settings: InAppWebViewSettings(
+ userAgent: 'Custom_User_Agent2',
+ ));
+
+ final String customUserAgent2 =
+ await controller1.evaluateJavascript(source: 'navigator.userAgent;');
+ expect(customUserAgent2, 'Custom_User_Agent2');
+ }, skip: shouldSkip);
+}
diff --git a/example/integration_test/in_app_webview/should_override_url_loading.dart b/example/integration_test/in_app_webview/should_override_url_loading.dart
new file mode 100644
index 00000000..bf776a86
--- /dev/null
+++ b/example/integration_test/in_app_webview/should_override_url_loading.dart
@@ -0,0 +1,250 @@
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_inappwebview/flutter_inappwebview.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../constants.dart';
+
+void shouldOverrideUrlLoading() {
+ final shouldSkip = kIsWeb ||
+ ![
+ TargetPlatform.android,
+ TargetPlatform.iOS,
+ TargetPlatform.macOS,
+ ].contains(defaultTargetPlatform);
+
+ group('shouldOverrideUrlLoading', () {
+ final String page =
+ '''flutter_inappwebview