diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml
new file mode 100644
index 000000000..3b0b4626d
--- /dev/null
+++ b/.idea/libraries/Dart_Packages.xml
@@ -0,0 +1,420 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml
index 65bb3679c..317997300 100755
--- a/.idea/libraries/Flutter_Plugins.xml
+++ b/.idea/libraries/Flutter_Plugins.xml
@@ -1,8 +1,6 @@
-
-
-
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc96fff30..d19becd87 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 5.3.2
+
+- Added `onLoad` and `onError` callbacks in `ScriptHtmlTagAttributes` class used by `InAppWebViewController.injectJavascriptFileFromUrl`
+- `InAppWebViewController.injectJavascriptFileFromAsset` returns a `Future` type now
+
## 5.3.1+1
- Removed duplicate lib exports
diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebView.java
index d8d827d0a..dcde91596 100755
--- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebView.java
+++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebView.java
@@ -1019,7 +1019,18 @@ public void injectJavascriptFileFromUrl(String urlFile, @Nullable Map();
final Completer pageLoaded = Completer();
+ final Completer jQueryLoaded = Completer();
+ final Completer jQueryLoadError = Completer();
await tester.pumpWidget(
Directionality(
@@ -4163,10 +4165,26 @@ setTimeout(function() {
await controllerCompleter.future;
await pageLoaded.future;
+ await controller.injectJavascriptFileFromUrl(
+ urlFile: Uri.parse('https://www.notawebsite..com/jquery-3.3.1.min.js'),
+ scriptHtmlTagAttributes: ScriptHtmlTagAttributes(id: 'jquery-error', onError: () {
+ jQueryLoadError.complete();
+ },));
+ await jQueryLoadError.future;
+ expect(
+ await controller.evaluateJavascript(
+ source: "document.body.querySelector('#jquery-error') == null;"),
+ false);
+ expect(
+ await controller.evaluateJavascript(source: "window.jQuery == null;"),
+ true);
+
await controller.injectJavascriptFileFromUrl(
urlFile: Uri.parse('https://code.jquery.com/jquery-3.3.1.min.js'),
- scriptHtmlTagAttributes: ScriptHtmlTagAttributes(id: 'jquery'));
- await Future.delayed(Duration(seconds: 4));
+ scriptHtmlTagAttributes: ScriptHtmlTagAttributes(id: 'jquery', onLoad: () {
+ jQueryLoaded.complete();
+ },));
+ await jQueryLoaded.future;
expect(
await controller.evaluateJavascript(
source: "document.body.querySelector('#jquery') == null;"),
diff --git a/example/ios/Flutter/flutter_export_environment.sh b/example/ios/Flutter/flutter_export_environment.sh
index ff2151af4..ff7134740 100755
--- a/example/ios/Flutter/flutter_export_environment.sh
+++ b/example/ios/Flutter/flutter_export_environment.sh
@@ -1,13 +1,13 @@
#!/bin/sh
# This is a generated file; do not edit or check into version control.
-export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter"
+export "FLUTTER_ROOT=/Users/lorenzopichilli/fvm/versions/2.1.0-10.0.pre"
export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example"
-export "FLUTTER_TARGET=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/lib/main.dart"
+export "FLUTTER_TARGET=integration_test/webview_flutter_test.dart"
export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
-export "DART_DEFINES=Zmx1dHRlci5pbnNwZWN0b3Iuc3RydWN0dXJlZEVycm9ycz10cnVl,RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ=="
+export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ=="
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"
diff --git a/flutter_inappwebview.iml b/flutter_inappwebview.iml
index 0adae5aa8..4cb391599 100755
--- a/flutter_inappwebview.iml
+++ b/flutter_inappwebview.iml
@@ -80,5 +80,6 @@
+
\ No newline at end of file
diff --git a/ios/Classes/InAppWebView/InAppWebView.swift b/ios/Classes/InAppWebView/InAppWebView.swift
index 12c8c40c8..f49fcef7d 100755
--- a/ios/Classes/InAppWebView/InAppWebView.swift
+++ b/ios/Classes/InAppWebView/InAppWebView.swift
@@ -1336,7 +1336,22 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
scriptAttributes += " script.type = '\(typeAttr.replacingOccurrences(of: "\'", with: "\\'"))'; "
}
if let idAttr = scriptHtmlTagAttributes["id"] as? String {
- scriptAttributes += " script.id = '\(idAttr.replacingOccurrences(of: "\'", with: "\\'"))'; "
+ let scriptIdEscaped = idAttr.replacingOccurrences(of: "\'", with: "\\'")
+ scriptAttributes += " script.id = '\(scriptIdEscaped)'; "
+ scriptAttributes += """
+ script.onload = function() {
+ if (window.\(JAVASCRIPT_BRIDGE_NAME) != null) {
+ window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onInjectedScriptLoaded', '\(scriptIdEscaped)');
+ }
+ };
+ """
+ scriptAttributes += """
+ script.onerror = function() {
+ if (window.\(JAVASCRIPT_BRIDGE_NAME) != null) {
+ window.\(JAVASCRIPT_BRIDGE_NAME).callHandler('onInjectedScriptError', '\(scriptIdEscaped)');
+ }
+ };
+ """
}
if let asyncAttr = scriptHtmlTagAttributes["async"] as? Bool, asyncAttr {
scriptAttributes += " script.async = true; "
diff --git a/lib/src/in_app_webview/in_app_webview_controller.dart b/lib/src/in_app_webview/in_app_webview_controller.dart
index 4db0084ef..d34072bbb 100644
--- a/lib/src/in_app_webview/in_app_webview_controller.dart
+++ b/lib/src/in_app_webview/in_app_webview_controller.dart
@@ -55,6 +55,7 @@ class InAppWebViewController {
HashMap();
List _userScripts = [];
Set _webMessageListenerObjNames = Set();
+ Map _injectedScriptsFromURL = {};
// ignore: unused_field
dynamic _id;
@@ -102,6 +103,7 @@ class InAppWebViewController {
Future handleMethod(MethodCall call) async {
switch (call.method) {
case "onLoadStart":
+ _injectedScriptsFromURL.clear();
if ((_webview != null && _webview!.onLoadStart != null) ||
_inAppBrowser != null) {
String? url = call.arguments["url"];
@@ -865,6 +867,22 @@ class InAppWebViewController {
_webview!.onWindowBlur!(this);
else if (_inAppBrowser != null) _inAppBrowser!.onWindowBlur();
return null;
+ case "onInjectedScriptLoaded":
+ String id = args[0];
+ var onLoadCallback = _injectedScriptsFromURL[id]?.onLoad;
+ if ((_webview != null || _inAppBrowser != null) &&
+ onLoadCallback != null) {
+ onLoadCallback();
+ }
+ return null;
+ case "onInjectedScriptError":
+ String id = args[0];
+ var onErrorCallback = _injectedScriptsFromURL[id]?.onError;
+ if ((_webview != null || _inAppBrowser != null) &&
+ onErrorCallback != null) {
+ onErrorCallback();
+ }
+ return null;
}
if (javaScriptHandlersMap.containsKey(handlerName)) {
@@ -1372,6 +1390,10 @@ class InAppWebViewController {
{required Uri urlFile,
ScriptHtmlTagAttributes? scriptHtmlTagAttributes}) async {
assert(urlFile.toString().isNotEmpty);
+ var id = scriptHtmlTagAttributes?.id;
+ if (scriptHtmlTagAttributes != null && id != null) {
+ _injectedScriptsFromURL[id] = scriptHtmlTagAttributes;
+ }
Map args = {};
args.putIfAbsent('urlFile', () => urlFile.toString());
args.putIfAbsent(
@@ -1385,10 +1407,10 @@ class InAppWebViewController {
///because, in these events, the [WebView] is not ready to handle it yet.
///Instead, you should call this method, for example, inside the [WebView.onLoadStop] event or in any other events
///where you know the page is ready "enough".
- Future injectJavascriptFileFromAsset(
+ Future injectJavascriptFileFromAsset(
{required String assetFilePath}) async {
String source = await rootBundle.loadString(assetFilePath);
- await evaluateJavascript(source: source);
+ return await evaluateJavascript(source: source);
}
///Injects CSS into the WebView.
@@ -2044,13 +2066,13 @@ class InAppWebViewController {
///[functionBody] is the JavaScript string to use as the function body.
///This method treats the string as an anonymous JavaScript function body and calls it with the named arguments in the arguments parameter.
///
- ///[arguments] is a dictionary of the arguments to pass to the function call.
- ///Each key in the dictionary corresponds to the name of an argument in the [functionBody] string,
+ ///[arguments] is a `Map` of the arguments to pass to the function call.
+ ///Each key in the `Map` corresponds to the name of an argument in the [functionBody] string,
///and the value of that key is the value to use during the evaluation of the code.
///Supported value types can be found in the official Flutter docs:
///[Platform channel data types support and codecs](https://flutter.dev/docs/development/platform-integration/platform-channels#codec),
///except for [Uint8List], [Int32List], [Int64List], and [Float64List] that should be converted into a [List].
- ///All items in an array or dictionary must also be one of the supported types.
+ ///All items in a `List` or `Map` must also be one of the supported types.
///
///[contentWorld], on iOS, it represents the namespace in which to evaluate the JavaScript [source] code.
///Instead, on Android, it will run the [source] code into an iframe.
@@ -2059,6 +2081,11 @@ class InAppWebViewController {
///For more information about content worlds, see [ContentWorld].
///Available on iOS 14.0+.
///
+ ///**NOTE**: This method shouldn't be called in the [WebView.onWebViewCreated] or [WebView.onLoadStart] events,
+ ///because, in these events, the [WebView] is not ready to handle it yet.
+ ///Instead, you should call this method, for example, inside the [WebView.onLoadStop] event or in any other events
+ ///where you know the page is ready "enough".
+ ///
///**NOTE for iOS**: available only on iOS 10.3+.
///
///**NOTE for Android**: available only on Android 21+.
diff --git a/lib/src/types.dart b/lib/src/types.dart
index 14a052743..eb9d1d68f 100755
--- a/lib/src/types.dart
+++ b/lib/src/types.dart
@@ -6037,6 +6037,16 @@ class ScriptHtmlTagAttributes {
///Indicates which referrer to send when fetching the script, or resources fetched by the script.
ReferrerPolicy? referrerPolicy;
+ ///Represents a callback function that will be called as soon as the script has been loaded successfully.
+ ///
+ ///**NOTE**: This callback requires the [id] property to be set.
+ Function()? onLoad;
+
+ ///Represents a callback function that will be called if an error occurred while trying to load the script.
+ ///
+ ///**NOTE**: This callback requires the [id] property to be set.
+ Function()? onError;
+
ScriptHtmlTagAttributes(
{this.type = "text/javascript",
this.id,
@@ -6046,7 +6056,14 @@ class ScriptHtmlTagAttributes {
this.integrity,
this.noModule,
this.nonce,
- this.referrerPolicy});
+ this.referrerPolicy,
+ this.onLoad,
+ this.onError}) {
+ if (this.onLoad != null || this.onError != null) {
+ assert(this.id != null,
+ 'onLoad and onError callbacks require the id property to be set.');
+ }
+ }
Map toMap() {
return {
diff --git a/pubspec.yaml b/pubspec.yaml
index 6059031d8..f810981bd 100755
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
-version: 5.3.1+1
+version: 5.3.2
homepage: https://github.com/pichillilorenzo/flutter_inappwebview
environment: