Throw an error if any controller is used after being disposed, created internal ChannelController class

This commit is contained in:
Lorenzo Pichilli 2023-11-11 13:18:02 +01:00
parent 612ed1eec3
commit a623ee718d
20 changed files with 368 additions and 365 deletions

View File

@ -1,6 +1,7 @@
## 6.0.0-beta.26 ## 6.0.0-beta.26
- Updated return value for `CookieManager.setCookie` method to be `Future<bool>`. The return value indicates whether the cookie was set successfully. - Throw an error if any controller is used after being disposed
- Updated return value for `CookieManager.setCookie` method to be `Future<bool>`. The return value indicates whether the cookie was set successfully
- Merged "feat(ios): optional tradeoff to fix ios input delay" [#1665](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1665) (thanks to [andreasgangso](https://github.com/andreasgangso)) - Merged "feat(ios): optional tradeoff to fix ios input delay" [#1665](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1665) (thanks to [andreasgangso](https://github.com/andreasgangso))
- Merged "Fix ios multiple flutter presenting error" [#1736](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1736) (thanks to [AlexT84](https://github.com/AlexT84)) - Merged "Fix ios multiple flutter presenting error" [#1736](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1736) (thanks to [AlexT84](https://github.com/AlexT84))
- Merged "fix cert parsing for ios 12" [#1822](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1822) (thanks to [darkang3lz92](https://github.com/darkang3lz92)) - Merged "fix cert parsing for ios 12" [#1822](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1822) (thanks to [darkang3lz92](https://github.com/darkang3lz92))

View File

@ -4,7 +4,6 @@ import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_i
import '../types/proxy_rule.dart'; import '../types/proxy_rule.dart';
import 'webview_feature.dart'; import 'webview_feature.dart';
import '../in_app_webview/webview.dart'; import '../in_app_webview/webview.dart';
import '../types/main.dart';
part 'proxy_controller.g.dart'; part 'proxy_controller.g.dart';

View File

@ -3,6 +3,7 @@ import 'dart:typed_data';
import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart'; import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart';
import 'chrome_safari_browser.dart'; import 'chrome_safari_browser.dart';
import 'chrome_safari_browser_menu_item.dart';
import '../web_uri.dart'; import '../web_uri.dart';
part 'chrome_safari_action_button.g.dart'; part 'chrome_safari_action_button.g.dart';

View File

@ -26,7 +26,7 @@ import 'chrome_safari_browser_secondary_toolbar.dart';
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Android ///- Android
///- iOS ///- iOS
class ChromeSafariBrowser { class ChromeSafariBrowser extends ChannelController {
///Debug settings. ///Debug settings.
static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings(); static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings();
@ -37,36 +37,23 @@ class ChromeSafariBrowser {
Map<int, ChromeSafariBrowserMenuItem> _menuItems = new HashMap(); Map<int, ChromeSafariBrowserMenuItem> _menuItems = new HashMap();
ChromeSafariBrowserSecondaryToolbar? _secondaryToolbar; ChromeSafariBrowserSecondaryToolbar? _secondaryToolbar;
bool _isOpened = false; bool _isOpened = false;
MethodChannel? _channel;
static const MethodChannel _sharedChannel = static const MethodChannel _sharedChannel =
const MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser'); const MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser');
ChromeSafariBrowser() { ChromeSafariBrowser() {
id = IdGenerator.generate(); id = IdGenerator.generate();
this._channel = channel =
MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser_$id'); MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser_$id');
this._channel?.setMethodCallHandler((call) async { handler = _handleMethod;
try { initMethodCallHandler();
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
_isOpened = false; _isOpened = false;
} }
_init() { _init() {
this._channel = channel =
MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser_$id'); MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser_$id');
this._channel?.setMethodCallHandler((call) async { handler = _handleMethod;
try { initMethodCallHandler();
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
} }
_debugLog(String method, dynamic args) { _debugLog(String method, dynamic args) {
@ -116,7 +103,7 @@ class ChromeSafariBrowser {
break; break;
case "onClosed": case "onClosed":
_isOpened = false; _isOpened = false;
_dispose(); dispose();
onClosed(); onClosed();
break; break;
case "onItemActionPerform": case "onItemActionPerform":
@ -196,8 +183,8 @@ class ChromeSafariBrowser {
List<WebUri>? otherLikelyURLs, List<WebUri>? otherLikelyURLs,
WebUri? referrer, WebUri? referrer,
@Deprecated('Use settings instead') @Deprecated('Use settings instead')
// ignore: deprecated_member_use_from_same_package // ignore: deprecated_member_use_from_same_package
ChromeSafariBrowserClassOptions? options, ChromeSafariBrowserClassOptions? options,
ChromeSafariBrowserSettings? settings}) async { ChromeSafariBrowserSettings? settings}) async {
assert(!_isOpened, 'The browser is already opened.'); assert(!_isOpened, 'The browser is already opened.');
_isOpened = true; _isOpened = true;
@ -262,7 +249,7 @@ class ChromeSafariBrowser {
args.putIfAbsent('otherLikelyURLs', args.putIfAbsent('otherLikelyURLs',
() => otherLikelyURLs?.map((e) => e.toString()).toList()); () => otherLikelyURLs?.map((e) => e.toString()).toList());
args.putIfAbsent('referrer', () => referrer?.toString()); args.putIfAbsent('referrer', () => referrer?.toString());
await _channel?.invokeMethod("launchUrl", args); await channel?.invokeMethod("launchUrl", args);
} }
///Tells the browser of a likely future navigation to a URL. ///Tells the browser of a likely future navigation to a URL.
@ -283,7 +270,7 @@ class ChromeSafariBrowser {
args.putIfAbsent('url', () => url?.toString()); args.putIfAbsent('url', () => url?.toString());
args.putIfAbsent('otherLikelyURLs', args.putIfAbsent('otherLikelyURLs',
() => otherLikelyURLs?.map((e) => e.toString()).toList()); () => otherLikelyURLs?.map((e) => e.toString()).toList());
return await _channel?.invokeMethod("mayLaunchUrl", args); return await channel?.invokeMethod<bool>("mayLaunchUrl", args) ?? false;
} }
///Requests to validate a relationship between the application and an origin. ///Requests to validate a relationship between the application and an origin.
@ -308,7 +295,7 @@ class ChromeSafariBrowser {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('relation', () => relation.toNativeValue()); args.putIfAbsent('relation', () => relation.toNativeValue());
args.putIfAbsent('origin', () => origin.toString()); args.putIfAbsent('origin', () => origin.toString());
return await _channel?.invokeMethod("validateRelationship", args); return await channel?.invokeMethod<bool>("validateRelationship", args) ?? false;
} }
///Closes the [ChromeSafariBrowser] instance. ///Closes the [ChromeSafariBrowser] instance.
@ -318,7 +305,7 @@ class ChromeSafariBrowser {
///- iOS ///- iOS
Future<void> close() async { Future<void> close() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod("close", args); await channel?.invokeMethod("close", args);
} }
///Set a custom action button. ///Set a custom action button.
@ -342,7 +329,7 @@ class ChromeSafariBrowser {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('icon', () => icon); args.putIfAbsent('icon', () => icon);
args.putIfAbsent('description', () => description); args.putIfAbsent('description', () => description);
await _channel?.invokeMethod("updateActionButton", args); await channel?.invokeMethod("updateActionButton", args);
_actionButton?.icon = icon; _actionButton?.icon = icon;
_actionButton?.description = description; _actionButton?.description = description;
} }
@ -368,7 +355,7 @@ class ChromeSafariBrowser {
ChromeSafariBrowserSecondaryToolbar secondaryToolbar) async { ChromeSafariBrowserSecondaryToolbar secondaryToolbar) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('secondaryToolbar', () => secondaryToolbar.toMap()); args.putIfAbsent('secondaryToolbar', () => secondaryToolbar.toMap());
await _channel?.invokeMethod("updateSecondaryToolbar", args); await channel?.invokeMethod("updateSecondaryToolbar", args);
this._secondaryToolbar = secondaryToolbar; this._secondaryToolbar = secondaryToolbar;
} }
@ -414,7 +401,7 @@ class ChromeSafariBrowser {
///- Android ///- Android
static Future<int> getMaxToolbarItems() async { static Future<int> getMaxToolbarItems() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _sharedChannel.invokeMethod("getMaxToolbarItems", args); return await _sharedChannel.invokeMethod<int>("getMaxToolbarItems", args) ?? 0;
} }
///Clear associated website data accrued from browsing activity within your app. ///Clear associated website data accrued from browsing activity within your app.
@ -543,8 +530,9 @@ class ChromeSafariBrowser {
} }
///Disposes the channel. ///Disposes the channel.
void _dispose() { @override
_channel?.setMethodCallHandler(null); @mustCallSuper
_channel = null; void dispose() {
disposeChannel();
} }
} }

View File

@ -8,9 +8,7 @@ import '../util.dart';
///- Android native WebView ///- Android native WebView
///- iOS ///- iOS
///- MacOS ///- MacOS
class FindInteractionController { class FindInteractionController extends ChannelController {
MethodChannel? _channel;
///Debug settings. ///Debug settings.
static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings(); static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings();
@ -81,7 +79,7 @@ class FindInteractionController {
Future<void> findAll({String? find}) async { Future<void> findAll({String? find}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('find', () => find); args.putIfAbsent('find', () => find);
await _channel?.invokeMethod('findAll', args); await channel?.invokeMethod('findAll', args);
} }
///Highlights and scrolls to the next match found by [findAll]. Notifies [FindInteractionController.onFindResultReceived] listener. ///Highlights and scrolls to the next match found by [findAll]. Notifies [FindInteractionController.onFindResultReceived] listener.
@ -99,7 +97,7 @@ class FindInteractionController {
Future<void> findNext({bool forward = true}) async { Future<void> findNext({bool forward = true}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('forward', () => forward); args.putIfAbsent('forward', () => forward);
await _channel?.invokeMethod('findNext', args); await channel?.invokeMethod('findNext', args);
} }
///Clears the highlighting surrounding text matches created by [findAll]. ///Clears the highlighting surrounding text matches created by [findAll].
@ -114,7 +112,7 @@ class FindInteractionController {
///- MacOS ///- MacOS
Future<void> clearMatches() async { Future<void> clearMatches() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('clearMatches', args); await channel?.invokeMethod('clearMatches', args);
} }
///Pre-populate the search text to be used. ///Pre-populate the search text to be used.
@ -129,7 +127,7 @@ class FindInteractionController {
Future<void> setSearchText(String? searchText) async { Future<void> setSearchText(String? searchText) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('searchText', () => searchText); args.putIfAbsent('searchText', () => searchText);
await _channel?.invokeMethod('setSearchText', args); await channel?.invokeMethod('setSearchText', args);
} }
///Get the search text used. ///Get the search text used.
@ -143,7 +141,7 @@ class FindInteractionController {
///- MacOS ///- MacOS
Future<String?> getSearchText() async { Future<String?> getSearchText() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('getSearchText', args); return await channel?.invokeMethod<String?>('getSearchText', args);
} }
///A Boolean value that indicates when the find panel displays onscreen. ///A Boolean value that indicates when the find panel displays onscreen.
@ -154,7 +152,7 @@ class FindInteractionController {
///- iOS ([Official API - UIFindInteraction.isFindNavigatorVisible](https://developer.apple.com/documentation/uikit/uifindinteraction/3975828-isfindnavigatorvisible?changes=_2)) ///- iOS ([Official API - UIFindInteraction.isFindNavigatorVisible](https://developer.apple.com/documentation/uikit/uifindinteraction/3975828-isfindnavigatorvisible?changes=_2))
Future<bool?> isFindNavigatorVisible() async { Future<bool?> isFindNavigatorVisible() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('isFindNavigatorVisible', args); return await channel?.invokeMethod<bool?>('isFindNavigatorVisible', args);
} }
///Updates the results the interface displays for the active search. ///Updates the results the interface displays for the active search.
@ -165,7 +163,7 @@ class FindInteractionController {
///- iOS ([Official API - UIFindInteraction.updateResultCount](https://developer.apple.com/documentation/uikit/uifindinteraction/3975835-updateresultcount?changes=_2)) ///- iOS ([Official API - UIFindInteraction.updateResultCount](https://developer.apple.com/documentation/uikit/uifindinteraction/3975835-updateresultcount?changes=_2))
Future<void> updateResultCount() async { Future<void> updateResultCount() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('updateResultCount', args); await channel?.invokeMethod('updateResultCount', args);
} }
///Begins a search, displaying the find panel. ///Begins a search, displaying the find panel.
@ -176,7 +174,7 @@ class FindInteractionController {
///- iOS ([Official API - UIFindInteraction.presentFindNavigator](https://developer.apple.com/documentation/uikit/uifindinteraction/3975832-presentfindnavigator?changes=_2)) ///- iOS ([Official API - UIFindInteraction.presentFindNavigator](https://developer.apple.com/documentation/uikit/uifindinteraction/3975832-presentfindnavigator?changes=_2))
Future<void> presentFindNavigator() async { Future<void> presentFindNavigator() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('presentFindNavigator', args); await channel?.invokeMethod('presentFindNavigator', args);
} }
///Dismisses the find panel, if present. ///Dismisses the find panel, if present.
@ -187,7 +185,7 @@ class FindInteractionController {
///- iOS ([Official API - UIFindInteraction.dismissFindNavigator](https://developer.apple.com/documentation/uikit/uifindinteraction/3975827-dismissfindnavigator?changes=_2)) ///- iOS ([Official API - UIFindInteraction.dismissFindNavigator](https://developer.apple.com/documentation/uikit/uifindinteraction/3975827-dismissfindnavigator?changes=_2))
Future<void> dismissFindNavigator() async { Future<void> dismissFindNavigator() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('dismissFindNavigator', args); await channel?.invokeMethod('dismissFindNavigator', args);
} }
///If there's a currently active find session, returns the active find session. ///If there's a currently active find session, returns the active find session.
@ -199,33 +197,23 @@ class FindInteractionController {
Future<FindSession?> getActiveFindSession() async { Future<FindSession?> getActiveFindSession() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
Map<String, dynamic>? result = Map<String, dynamic>? result =
(await _channel?.invokeMethod('getActiveFindSession', args)) (await channel?.invokeMethod('getActiveFindSession', args))
?.cast<String, dynamic>(); ?.cast<String, dynamic>();
return FindSession.fromMap(result); return FindSession.fromMap(result);
} }
///Disposes the controller. ///Disposes the controller.
@override
void dispose({bool isKeepAlive = false}) { void dispose({bool isKeepAlive = false}) {
if (!isKeepAlive) { disposeChannel(removeMethodCallHandler: !isKeepAlive);
_channel?.setMethodCallHandler(null);
}
_channel = null;
} }
} }
extension InternalFindInteractionController on FindInteractionController { extension InternalFindInteractionController on FindInteractionController {
void init(dynamic id) { void init(dynamic id) {
this._channel = MethodChannel( channel = MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_find_interaction_$id'); 'com.pichillilorenzo/flutter_inappwebview_find_interaction_$id');
handler = _handleMethod;
this._channel?.setMethodCallHandler((call) async { initMethodCallHandler();
if (_channel == null) return null;
try {
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
} }
} }

View File

@ -3,6 +3,7 @@ import 'dart:collection';
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import '../context_menu/context_menu.dart'; import '../context_menu/context_menu.dart';
@ -28,7 +29,7 @@ import '../pull_to_refresh/pull_to_refresh_controller.dart';
///- Android native WebView ///- Android native WebView
///- iOS ///- iOS
///- MacOS ///- MacOS
class InAppBrowser { class InAppBrowser extends ChannelController {
///Debug settings. ///Debug settings.
static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings(); static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings();
@ -48,7 +49,6 @@ class InAppBrowser {
final UnmodifiableListView<UserScript>? initialUserScripts; final UnmodifiableListView<UserScript>? initialUserScripts;
bool _isOpened = false; bool _isOpened = false;
MethodChannel? _channel;
static const MethodChannel _sharedChannel = static const MethodChannel _sharedChannel =
const MethodChannel('com.pichillilorenzo/flutter_inappbrowser'); const MethodChannel('com.pichillilorenzo/flutter_inappbrowser');
@ -70,18 +70,13 @@ class InAppBrowser {
} }
_init() { _init() {
this._channel = channel =
MethodChannel('com.pichillilorenzo/flutter_inappbrowser_$id'); MethodChannel('com.pichillilorenzo/flutter_inappbrowser_$id');
this._channel?.setMethodCallHandler((call) async { handler = _handleMethod;
try { initMethodCallHandler();
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
_webViewController = new InAppWebViewController.fromInAppBrowser( _webViewController = new InAppWebViewController.fromInAppBrowser(
this._channel!, this, this.initialUserScripts); channel!, this, this.initialUserScripts);
pullToRefreshController?.init(id); pullToRefreshController?.init(id);
findInteractionController?.init(id); findInteractionController?.init(id);
} }
@ -113,7 +108,7 @@ class InAppBrowser {
case "onExit": case "onExit":
_debugLog(call.method, call.arguments); _debugLog(call.method, call.arguments);
_isOpened = false; _isOpened = false;
_dispose(); dispose();
onExit(); onExit();
break; break;
default: default:
@ -366,7 +361,7 @@ class InAppBrowser {
assert(_isOpened, 'The browser is not opened.'); assert(_isOpened, 'The browser is not opened.');
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('show', args); await channel?.invokeMethod('show', args);
} }
///Hides the [InAppBrowser] window. Calling this has no effect if the [InAppBrowser] was already hidden. ///Hides the [InAppBrowser] window. Calling this has no effect if the [InAppBrowser] was already hidden.
@ -379,7 +374,7 @@ class InAppBrowser {
assert(_isOpened, 'The browser is not opened.'); assert(_isOpened, 'The browser is not opened.');
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('hide', args); await channel?.invokeMethod('hide', args);
} }
///Closes the [InAppBrowser] window. ///Closes the [InAppBrowser] window.
@ -392,7 +387,7 @@ class InAppBrowser {
assert(_isOpened, 'The browser is not opened.'); assert(_isOpened, 'The browser is not opened.');
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('close', args); await channel?.invokeMethod('close', args);
} }
///Check if the Web View of the [InAppBrowser] instance is hidden. ///Check if the Web View of the [InAppBrowser] instance is hidden.
@ -405,7 +400,7 @@ class InAppBrowser {
assert(_isOpened, 'The browser is not opened.'); assert(_isOpened, 'The browser is not opened.');
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('isHidden', args); return await channel?.invokeMethod<bool>('isHidden', args) ?? false;
} }
///Use [setSettings] instead. ///Use [setSettings] instead.
@ -415,7 +410,7 @@ class InAppBrowser {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('settings', () => options.toMap()); args.putIfAbsent('settings', () => options.toMap());
await _channel?.invokeMethod('setSettings', args); await channel?.invokeMethod('setSettings', args);
} }
///Use [getSettings] instead. ///Use [getSettings] instead.
@ -425,7 +420,7 @@ class InAppBrowser {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
Map<dynamic, dynamic>? options = Map<dynamic, dynamic>? options =
await _channel?.invokeMethod('getSettings', args); await channel?.invokeMethod('getSettings', args);
if (options != null) { if (options != null) {
options = options.cast<String, dynamic>(); options = options.cast<String, dynamic>();
return InAppBrowserClassOptions.fromMap(options as Map<String, dynamic>); return InAppBrowserClassOptions.fromMap(options as Map<String, dynamic>);
@ -446,7 +441,7 @@ class InAppBrowser {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('settings', () => settings.toMap()); args.putIfAbsent('settings', () => settings.toMap());
await _channel?.invokeMethod('setSettings', args); await channel?.invokeMethod('setSettings', args);
} }
///Gets the current [InAppBrowser] settings. Returns `null` if it wasn't able to get them. ///Gets the current [InAppBrowser] settings. Returns `null` if it wasn't able to get them.
@ -461,7 +456,7 @@ class InAppBrowser {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
Map<dynamic, dynamic>? settings = Map<dynamic, dynamic>? settings =
await _channel?.invokeMethod('getSettings', args); await channel?.invokeMethod('getSettings', args);
if (settings != null) { if (settings != null) {
settings = settings.cast<String, dynamic>(); settings = settings.cast<String, dynamic>();
return InAppBrowserClassSettings.fromMap( return InAppBrowserClassSettings.fromMap(
@ -1361,9 +1356,10 @@ class InAppBrowser {
void onContentSizeChanged(Size oldContentSize, Size newContentSize) {} void onContentSizeChanged(Size oldContentSize, Size newContentSize) {}
///Disposes the channel and controllers. ///Disposes the channel and controllers.
void _dispose() { @override
_channel?.setMethodCallHandler(null); @mustCallSuper
_channel = null; void dispose() {
disposeChannel();
_webViewController?.dispose(); _webViewController?.dispose();
_webViewController = null; _webViewController = null;
pullToRefreshController?.dispose(); pullToRefreshController?.dispose();

View File

@ -29,7 +29,7 @@ import '../types/disposable.dart';
///- Web ///- Web
///- MacOS ///- MacOS
///{@endtemplate} ///{@endtemplate}
class HeadlessInAppWebView implements WebView, Disposable { class HeadlessInAppWebView extends ChannelController implements WebView, Disposable {
///View ID. ///View ID.
late final String id; late final String id;
@ -38,7 +38,6 @@ class HeadlessInAppWebView implements WebView, Disposable {
static const MethodChannel _sharedChannel = static const MethodChannel _sharedChannel =
const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview'); const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview');
MethodChannel? _channel;
InAppWebViewController? _webViewController; InAppWebViewController? _webViewController;
@ -187,19 +186,13 @@ class HeadlessInAppWebView implements WebView, Disposable {
_webViewController = InAppWebViewController(id, this); _webViewController = InAppWebViewController(id, this);
pullToRefreshController?.init(id); pullToRefreshController?.init(id);
findInteractionController?.init(id); findInteractionController?.init(id);
this._channel = channel =
MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview_$id'); MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview_$id');
this._channel?.setMethodCallHandler((call) async { handler = _handleMethod;
try { initMethodCallHandler();
return await handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
} }
Future<dynamic> handleMethod(MethodCall call) async { Future<dynamic> _handleMethod(MethodCall call) async {
switch (call.method) { switch (call.method) {
case "onWebViewCreated": case "onWebViewCreated":
if (onWebViewCreated != null && _webViewController != null) { if (onWebViewCreated != null && _webViewController != null) {
@ -310,9 +303,8 @@ class HeadlessInAppWebView implements WebView, Disposable {
return; return;
} }
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('dispose', args); await channel?.invokeMethod('dispose', args);
_channel?.setMethodCallHandler(null); disposeChannel();
_channel = null;
_started = false; _started = false;
_running = false; _running = false;
_webViewController?.dispose(); _webViewController?.dispose();
@ -353,7 +345,7 @@ class HeadlessInAppWebView implements WebView, Disposable {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('size', () => size.toMap()); args.putIfAbsent('size', () => size.toMap());
await _channel?.invokeMethod('setSize', args); await channel?.invokeMethod('setSize', args);
} }
///Gets the current size in pixels of the WebView. ///Gets the current size in pixels of the WebView.
@ -372,7 +364,7 @@ class HeadlessInAppWebView implements WebView, Disposable {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
Map<String, dynamic> sizeMap = Map<String, dynamic> sizeMap =
(await _channel?.invokeMethod('getSize', args)) (await channel?.invokeMethod('getSize', args))
?.cast<String, dynamic>(); ?.cast<String, dynamic>();
return MapSize.fromMap(sizeMap); return MapSize.fromMap(sizeMap);
} }

View File

@ -50,12 +50,11 @@ final _JAVASCRIPT_HANDLER_FORBIDDEN_NAMES = UnmodifiableListView<String>([
/// ///
///If you are using the [InAppWebView] widget, an [InAppWebViewController] instance can be obtained by setting the [InAppWebView.onWebViewCreated] ///If you are using the [InAppWebView] widget, an [InAppWebViewController] instance can be obtained by setting the [InAppWebView.onWebViewCreated]
///callback. Instead, if you are using an [InAppBrowser] instance, you can get it through the [InAppBrowser.webViewController] attribute. ///callback. Instead, if you are using an [InAppBrowser] instance, you can get it through the [InAppBrowser.webViewController] attribute.
class InAppWebViewController { class InAppWebViewController extends ChannelController {
WebView? _webview; WebView? _webview;
MethodChannel? _channel;
static final MethodChannel _staticChannel = IN_APP_WEBVIEW_STATIC_CHANNEL; static final MethodChannel _staticChannel = IN_APP_WEBVIEW_STATIC_CHANNEL;
// properties to be saved and restored for keep alive feature // List of properties to be saved and restored for keep alive feature
Map<String, JavaScriptHandlerCallback> _javaScriptHandlersMap = Map<String, JavaScriptHandlerCallback> _javaScriptHandlersMap =
HashMap<String, JavaScriptHandlerCallback>(); HashMap<String, JavaScriptHandlerCallback>();
Map<UserScriptInjectionTime, List<UserScript>> _userScripts = { Map<UserScriptInjectionTime, List<UserScript>> _userScripts = {
@ -64,6 +63,8 @@ class InAppWebViewController {
}; };
Set<String> _webMessageListenerObjNames = Set(); Set<String> _webMessageListenerObjNames = Set();
Map<String, ScriptHtmlTagAttributes> _injectedScriptsFromURL = {}; Map<String, ScriptHtmlTagAttributes> _injectedScriptsFromURL = {};
Set<WebMessageChannel> _webMessageChannels = Set();
Set<WebMessageListener> _webMessageListeners = Set();
// static map that contains the properties to be saved and restored for keep alive feature // static map that contains the properties to be saved and restored for keep alive feature
static final Map<InAppWebViewKeepAlive, InAppWebViewControllerKeepAliveProps?> static final Map<InAppWebViewKeepAlive, InAppWebViewControllerKeepAliveProps?>
@ -86,17 +87,10 @@ class InAppWebViewController {
InAppWebViewController(dynamic id, WebView webview) { InAppWebViewController(dynamic id, WebView webview) {
this._id = id; this._id = id;
this._channel = channel = MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id');
MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id'); handler = handleMethod;
this._channel?.setMethodCallHandler((call) async { initMethodCallHandler();
if (_channel == null) return null;
try {
return await handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
this._webview = webview; this._webview = webview;
final initialUserScripts = webview.initialUserScripts; final initialUserScripts = webview.initialUserScripts;
@ -122,7 +116,7 @@ class InAppWebViewController {
MethodChannel channel, MethodChannel channel,
InAppBrowser inAppBrowser, InAppBrowser inAppBrowser,
UnmodifiableListView<UserScript>? initialUserScripts) { UnmodifiableListView<UserScript>? initialUserScripts) {
this._channel = channel; this.channel = channel;
this._inAppBrowser = inAppBrowser; this._inAppBrowser = inAppBrowser;
if (initialUserScripts != null) { if (initialUserScripts != null) {
@ -143,8 +137,8 @@ class InAppWebViewController {
} }
void _init() { void _init() {
android = AndroidInAppWebViewController(channel: _channel!); android = AndroidInAppWebViewController(channel: channel!);
ios = IOSInAppWebViewController(channel: _channel!); ios = IOSInAppWebViewController(channel: channel!);
webStorage = WebStorage( webStorage = WebStorage(
localStorage: LocalStorage(this), sessionStorage: SessionStorage(this)); localStorage: LocalStorage(this), sessionStorage: SessionStorage(this));
@ -158,13 +152,17 @@ class InAppWebViewController {
injectedScriptsFromURL: _injectedScriptsFromURL, injectedScriptsFromURL: _injectedScriptsFromURL,
javaScriptHandlersMap: _javaScriptHandlersMap, javaScriptHandlersMap: _javaScriptHandlersMap,
userScripts: _userScripts, userScripts: _userScripts,
webMessageListenerObjNames: _webMessageListenerObjNames); webMessageListenerObjNames: _webMessageListenerObjNames,
webMessageChannels: _webMessageChannels,
webMessageListeners: _webMessageListeners);
} else { } else {
// restore controller properties // restore controller properties
_injectedScriptsFromURL = props.injectedScriptsFromURL; _injectedScriptsFromURL = props.injectedScriptsFromURL;
_javaScriptHandlersMap = props.javaScriptHandlersMap; _javaScriptHandlersMap = props.javaScriptHandlersMap;
_userScripts = props.userScripts; _userScripts = props.userScripts;
_webMessageListenerObjNames = props.webMessageListenerObjNames; _webMessageListenerObjNames = props.webMessageListenerObjNames;
_webMessageChannels = props.webMessageChannels;
_webMessageListeners = props.webMessageListeners;
} }
} }
} }
@ -180,7 +178,7 @@ class InAppWebViewController {
args: args); args: args);
} }
Future<dynamic> handleMethod(MethodCall call) async { Future<dynamic> _handleMethod(MethodCall call) async {
if (WebView.debugLoggingSettings.enabled && if (WebView.debugLoggingSettings.enabled &&
call.method != "onCallJsHandler") { call.method != "onCallJsHandler") {
_debugLog(call.method, call.arguments); _debugLog(call.method, call.arguments);
@ -1430,7 +1428,7 @@ class InAppWebViewController {
///- Web ///- Web
Future<WebUri?> getUrl() async { Future<WebUri?> getUrl() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
String? url = await _channel?.invokeMethod('getUrl', args); String? url = await channel?.invokeMethod<String?>('getUrl', args);
return url != null ? WebUri(url) : null; return url != null ? WebUri(url) : null;
} }
@ -1445,7 +1443,7 @@ class InAppWebViewController {
///- Web ///- Web
Future<String?> getTitle() async { Future<String?> getTitle() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('getTitle', args); return await channel?.invokeMethod<String?>('getTitle', args);
} }
///Gets the progress for the current page. The progress value is between 0 and 100. ///Gets the progress for the current page. The progress value is between 0 and 100.
@ -1456,7 +1454,7 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.estimatedProgress](https://developer.apple.com/documentation/webkit/wkwebview/1415007-estimatedprogress)) ///- MacOS ([Official API - WKWebView.estimatedProgress](https://developer.apple.com/documentation/webkit/wkwebview/1415007-estimatedprogress))
Future<int?> getProgress() async { Future<int?> getProgress() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('getProgress', args); return await channel?.invokeMethod<int?>('getProgress', args);
} }
///Gets the content html of the page. It first tries to get the content through javascript. ///Gets the content html of the page. It first tries to get the content through javascript.
@ -1704,7 +1702,7 @@ class InAppWebViewController {
Future<void> loadUrl( Future<void> loadUrl(
{required URLRequest urlRequest, {required URLRequest urlRequest,
@Deprecated('Use allowingReadAccessTo instead') @Deprecated('Use allowingReadAccessTo instead')
Uri? iosAllowingReadAccessTo, Uri? iosAllowingReadAccessTo,
WebUri? allowingReadAccessTo}) async { WebUri? allowingReadAccessTo}) async {
assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty); assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty);
assert( assert(
@ -1719,7 +1717,7 @@ class InAppWebViewController {
() => () =>
allowingReadAccessTo?.toString() ?? allowingReadAccessTo?.toString() ??
iosAllowingReadAccessTo?.toString()); iosAllowingReadAccessTo?.toString());
await _channel?.invokeMethod('loadUrl', args); await channel?.invokeMethod('loadUrl', args);
} }
///Loads the given [url] with [postData] (x-www-form-urlencoded) using `POST` method into this WebView. ///Loads the given [url] with [postData] (x-www-form-urlencoded) using `POST` method into this WebView.
@ -1743,7 +1741,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url.toString()); args.putIfAbsent('url', () => url.toString());
args.putIfAbsent('postData', () => postData); args.putIfAbsent('postData', () => postData);
await _channel?.invokeMethod('postUrl', args); await channel?.invokeMethod('postUrl', args);
} }
///Loads the given [data] into this WebView, using [baseUrl] as the base URL for the content. ///Loads the given [data] into this WebView, using [baseUrl] as the base URL for the content.
@ -1770,11 +1768,10 @@ class InAppWebViewController {
String mimeType = "text/html", String mimeType = "text/html",
String encoding = "utf8", String encoding = "utf8",
WebUri? baseUrl, WebUri? baseUrl,
@Deprecated('Use historyUrl instead') @Deprecated('Use historyUrl instead') Uri? androidHistoryUrl,
Uri? androidHistoryUrl,
WebUri? historyUrl, WebUri? historyUrl,
@Deprecated('Use allowingReadAccessTo instead') @Deprecated('Use allowingReadAccessTo instead')
Uri? iosAllowingReadAccessTo, Uri? iosAllowingReadAccessTo,
WebUri? allowingReadAccessTo}) async { WebUri? allowingReadAccessTo}) async {
assert( assert(
allowingReadAccessTo == null || allowingReadAccessTo.isScheme("file")); allowingReadAccessTo == null || allowingReadAccessTo.isScheme("file"));
@ -1797,7 +1794,7 @@ class InAppWebViewController {
() => () =>
allowingReadAccessTo?.toString() ?? allowingReadAccessTo?.toString() ??
iosAllowingReadAccessTo?.toString()); iosAllowingReadAccessTo?.toString());
await _channel?.invokeMethod('loadData', args); await channel?.invokeMethod('loadData', args);
} }
///Loads the given [assetFilePath]. ///Loads the given [assetFilePath].
@ -1839,7 +1836,7 @@ class InAppWebViewController {
assert(assetFilePath.isNotEmpty); assert(assetFilePath.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('assetFilePath', () => assetFilePath); args.putIfAbsent('assetFilePath', () => assetFilePath);
await _channel?.invokeMethod('loadFile', args); await channel?.invokeMethod('loadFile', args);
} }
///Reloads the WebView. ///Reloads the WebView.
@ -1853,7 +1850,7 @@ class InAppWebViewController {
///- Web ([Official API - Location.reload](https://developer.mozilla.org/en-US/docs/Web/API/Location/reload)) ///- Web ([Official API - Location.reload](https://developer.mozilla.org/en-US/docs/Web/API/Location/reload))
Future<void> reload() async { Future<void> reload() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('reload', args); await channel?.invokeMethod('reload', args);
} }
///Goes back in the history of the WebView. ///Goes back in the history of the WebView.
@ -1867,7 +1864,7 @@ class InAppWebViewController {
///- Web ([Official API - History.back](https://developer.mozilla.org/en-US/docs/Web/API/History/back)) ///- Web ([Official API - History.back](https://developer.mozilla.org/en-US/docs/Web/API/History/back))
Future<void> goBack() async { Future<void> goBack() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('goBack', args); await channel?.invokeMethod('goBack', args);
} }
///Returns a boolean value indicating whether the WebView can move backward. ///Returns a boolean value indicating whether the WebView can move backward.
@ -1878,7 +1875,7 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.canGoBack](https://developer.apple.com/documentation/webkit/wkwebview/1414966-cangoback)) ///- MacOS ([Official API - WKWebView.canGoBack](https://developer.apple.com/documentation/webkit/wkwebview/1414966-cangoback))
Future<bool> canGoBack() async { Future<bool> canGoBack() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('canGoBack', args); return await channel?.invokeMethod<bool>('canGoBack', args) ?? false;
} }
///Goes forward in the history of the WebView. ///Goes forward in the history of the WebView.
@ -1892,7 +1889,7 @@ class InAppWebViewController {
///- Web ([Official API - History.forward](https://developer.mozilla.org/en-US/docs/Web/API/History/forward)) ///- Web ([Official API - History.forward](https://developer.mozilla.org/en-US/docs/Web/API/History/forward))
Future<void> goForward() async { Future<void> goForward() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('goForward', args); await channel?.invokeMethod('goForward', args);
} }
///Returns a boolean value indicating whether the WebView can move forward. ///Returns a boolean value indicating whether the WebView can move forward.
@ -1903,7 +1900,7 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.canGoForward](https://developer.apple.com/documentation/webkit/wkwebview/1414962-cangoforward)) ///- MacOS ([Official API - WKWebView.canGoForward](https://developer.apple.com/documentation/webkit/wkwebview/1414962-cangoforward))
Future<bool> canGoForward() async { Future<bool> canGoForward() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('canGoForward', args); return await channel?.invokeMethod<bool>('canGoForward', args) ?? false;
} }
///Goes to the history item that is the number of steps away from the current item. Steps is negative if backward and positive if forward. ///Goes to the history item that is the number of steps away from the current item. Steps is negative if backward and positive if forward.
@ -1918,7 +1915,7 @@ class InAppWebViewController {
Future<void> goBackOrForward({required int steps}) async { Future<void> goBackOrForward({required int steps}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('steps', () => steps); args.putIfAbsent('steps', () => steps);
await _channel?.invokeMethod('goBackOrForward', args); await channel?.invokeMethod('goBackOrForward', args);
} }
///Returns a boolean value indicating whether the WebView can go back or forward the given number of steps. Steps is negative if backward and positive if forward. ///Returns a boolean value indicating whether the WebView can go back or forward the given number of steps. Steps is negative if backward and positive if forward.
@ -1930,7 +1927,8 @@ class InAppWebViewController {
Future<bool> canGoBackOrForward({required int steps}) async { Future<bool> canGoBackOrForward({required int steps}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('steps', () => steps); args.putIfAbsent('steps', () => steps);
return await _channel?.invokeMethod('canGoBackOrForward', args); return await channel?.invokeMethod<bool>('canGoBackOrForward', args) ??
false;
} }
///Navigates to a [WebHistoryItem] from the back-forward [WebHistory.list] and sets it as the current item. ///Navigates to a [WebHistoryItem] from the back-forward [WebHistory.list] and sets it as the current item.
@ -1958,7 +1956,7 @@ class InAppWebViewController {
///- Web ///- Web
Future<bool> isLoading() async { Future<bool> isLoading() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('isLoading', args); return await channel?.invokeMethod<bool>('isLoading', args) ?? false;
} }
///Stops the WebView from loading. ///Stops the WebView from loading.
@ -1972,7 +1970,7 @@ class InAppWebViewController {
///- Web ([Official API - Window.stop](https://developer.mozilla.org/en-US/docs/Web/API/Window/stop)) ///- Web ([Official API - Window.stop](https://developer.mozilla.org/en-US/docs/Web/API/Window/stop))
Future<void> stopLoading() async { Future<void> stopLoading() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('stopLoading', args); await channel?.invokeMethod('stopLoading', args);
} }
///Evaluates JavaScript [source] code into the WebView and returns the result of the evaluation. ///Evaluates JavaScript [source] code into the WebView and returns the result of the evaluation.
@ -2002,7 +2000,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('source', () => source); args.putIfAbsent('source', () => source);
args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); args.putIfAbsent('contentWorld', () => contentWorld?.toMap());
var data = await _channel?.invokeMethod('evaluateJavascript', args); var data = await channel?.invokeMethod('evaluateJavascript', args);
if (data != null && (Util.isAndroid || Util.isWeb)) { if (data != null && (Util.isAndroid || Util.isWeb)) {
try { try {
// try to json decode the data coming from JavaScript // try to json decode the data coming from JavaScript
@ -2041,7 +2039,7 @@ class InAppWebViewController {
args.putIfAbsent('urlFile', () => urlFile.toString()); args.putIfAbsent('urlFile', () => urlFile.toString());
args.putIfAbsent( args.putIfAbsent(
'scriptHtmlTagAttributes', () => scriptHtmlTagAttributes?.toMap()); 'scriptHtmlTagAttributes', () => scriptHtmlTagAttributes?.toMap());
await _channel?.invokeMethod('injectJavascriptFileFromUrl', args); await channel?.invokeMethod('injectJavascriptFileFromUrl', args);
} }
///Evaluates the content of a JavaScript file into the WebView from the flutter assets directory. ///Evaluates the content of a JavaScript file into the WebView from the flutter assets directory.
@ -2081,7 +2079,7 @@ class InAppWebViewController {
Future<void> injectCSSCode({required String source}) async { Future<void> injectCSSCode({required String source}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('source', () => source); args.putIfAbsent('source', () => source);
await _channel?.invokeMethod('injectCSSCode', args); await channel?.invokeMethod('injectCSSCode', args);
} }
///Injects an external CSS file into the WebView from a defined url. ///Injects an external CSS file into the WebView from a defined url.
@ -2108,7 +2106,7 @@ class InAppWebViewController {
args.putIfAbsent('urlFile', () => urlFile.toString()); args.putIfAbsent('urlFile', () => urlFile.toString());
args.putIfAbsent( args.putIfAbsent(
'cssLinkHtmlTagAttributes', () => cssLinkHtmlTagAttributes?.toMap()); 'cssLinkHtmlTagAttributes', () => cssLinkHtmlTagAttributes?.toMap());
await _channel?.invokeMethod('injectCSSFileFromUrl', args); await channel?.invokeMethod('injectCSSFileFromUrl', args);
} }
///Injects a CSS file into the WebView from the flutter assets directory. ///Injects a CSS file into the WebView from the flutter assets directory.
@ -2233,7 +2231,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent( args.putIfAbsent(
'screenshotConfiguration', () => screenshotConfiguration?.toMap()); 'screenshotConfiguration', () => screenshotConfiguration?.toMap());
return await _channel?.invokeMethod('takeScreenshot', args); return await channel?.invokeMethod<Uint8List?>('takeScreenshot', args);
} }
///Use [setSettings] instead. ///Use [setSettings] instead.
@ -2269,7 +2267,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('settings', () => settings.toMap()); args.putIfAbsent('settings', () => settings.toMap());
await _channel?.invokeMethod('setSettings', args); await channel?.invokeMethod('setSettings', args);
} }
///Gets the current WebView settings. Returns `null` if it wasn't able to get them. ///Gets the current WebView settings. Returns `null` if it wasn't able to get them.
@ -2283,7 +2281,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
Map<dynamic, dynamic>? settings = Map<dynamic, dynamic>? settings =
await _channel?.invokeMethod('getSettings', args); await channel?.invokeMethod('getSettings', args);
if (settings != null) { if (settings != null) {
settings = settings.cast<String, dynamic>(); settings = settings.cast<String, dynamic>();
return InAppWebViewSettings.fromMap(settings as Map<String, dynamic>); return InAppWebViewSettings.fromMap(settings as Map<String, dynamic>);
@ -2304,7 +2302,7 @@ class InAppWebViewController {
Future<WebHistory?> getCopyBackForwardList() async { Future<WebHistory?> getCopyBackForwardList() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
Map<String, dynamic>? result = Map<String, dynamic>? result =
(await _channel?.invokeMethod('getCopyBackForwardList', args)) (await channel?.invokeMethod('getCopyBackForwardList', args))
?.cast<String, dynamic>(); ?.cast<String, dynamic>();
return WebHistory.fromMap(result); return WebHistory.fromMap(result);
} }
@ -2317,7 +2315,7 @@ class InAppWebViewController {
///- MacOS ///- MacOS
Future<void> clearCache() async { Future<void> clearCache() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('clearCache', args); await channel?.invokeMethod('clearCache', args);
} }
///Use [FindInteractionController.findAll] instead. ///Use [FindInteractionController.findAll] instead.
@ -2325,7 +2323,7 @@ class InAppWebViewController {
Future<void> findAllAsync({required String find}) async { Future<void> findAllAsync({required String find}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('find', () => find); args.putIfAbsent('find', () => find);
await _channel?.invokeMethod('findAll', args); await channel?.invokeMethod('findAll', args);
} }
///Use [FindInteractionController.findNext] instead. ///Use [FindInteractionController.findNext] instead.
@ -2333,14 +2331,14 @@ class InAppWebViewController {
Future<void> findNext({required bool forward}) async { Future<void> findNext({required bool forward}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('forward', () => forward); args.putIfAbsent('forward', () => forward);
await _channel?.invokeMethod('findNext', args); await channel?.invokeMethod('findNext', args);
} }
///Use [FindInteractionController.clearMatches] instead. ///Use [FindInteractionController.clearMatches] instead.
@Deprecated("Use FindInteractionController.clearMatches instead") @Deprecated("Use FindInteractionController.clearMatches instead")
Future<void> clearMatches() async { Future<void> clearMatches() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('clearMatches', args); await channel?.invokeMethod('clearMatches', args);
} }
///Use [tRexRunnerHtml] instead. ///Use [tRexRunnerHtml] instead.
@ -2378,7 +2376,7 @@ class InAppWebViewController {
args.putIfAbsent('x', () => x); args.putIfAbsent('x', () => x);
args.putIfAbsent('y', () => y); args.putIfAbsent('y', () => y);
args.putIfAbsent('animated', () => animated); args.putIfAbsent('animated', () => animated);
await _channel?.invokeMethod('scrollTo', args); await channel?.invokeMethod('scrollTo', args);
} }
///Moves the scrolled position of the WebView. ///Moves the scrolled position of the WebView.
@ -2404,7 +2402,7 @@ class InAppWebViewController {
args.putIfAbsent('x', () => x); args.putIfAbsent('x', () => x);
args.putIfAbsent('y', () => y); args.putIfAbsent('y', () => y);
args.putIfAbsent('animated', () => animated); args.putIfAbsent('animated', () => animated);
await _channel?.invokeMethod('scrollBy', args); await channel?.invokeMethod('scrollBy', args);
} }
///On Android native WebView, it pauses all layout, parsing, and JavaScript timers for all WebViews. ///On Android native WebView, it pauses all layout, parsing, and JavaScript timers for all WebViews.
@ -2420,7 +2418,7 @@ class InAppWebViewController {
///- MacOS ///- MacOS
Future<void> pauseTimers() async { Future<void> pauseTimers() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('pauseTimers', args); await channel?.invokeMethod('pauseTimers', args);
} }
///On Android, it resumes all layout, parsing, and JavaScript timers for all WebViews. This will resume dispatching all timers. ///On Android, it resumes all layout, parsing, and JavaScript timers for all WebViews. This will resume dispatching all timers.
@ -2435,7 +2433,7 @@ class InAppWebViewController {
///- MacOS ///- MacOS
Future<void> resumeTimers() async { Future<void> resumeTimers() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('resumeTimers', args); await channel?.invokeMethod('resumeTimers', args);
} }
///Prints the current page. ///Prints the current page.
@ -2458,7 +2456,8 @@ class InAppWebViewController {
{PrintJobSettings? settings}) async { {PrintJobSettings? settings}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("settings", () => settings?.toMap()); args.putIfAbsent("settings", () => settings?.toMap());
String? jobId = await _channel?.invokeMethod('printCurrentPage', args); String? jobId =
await channel?.invokeMethod<String?>('printCurrentPage', args);
if (jobId != null) { if (jobId != null) {
return PrintJobController(id: jobId); return PrintJobController(id: jobId);
} }
@ -2478,7 +2477,7 @@ class InAppWebViewController {
///- Web ([Official API - Document.documentElement.scrollHeight](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight)) ///- Web ([Official API - Document.documentElement.scrollHeight](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight))
Future<int?> getContentHeight() async { Future<int?> getContentHeight() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
var height = await _channel?.invokeMethod('getContentHeight', args); var height = await channel?.invokeMethod('getContentHeight', args);
if (height == null || height == 0) { if (height == null || height == 0) {
// try to use javascript // try to use javascript
var scrollHeight = await evaluateJavascript( var scrollHeight = await evaluateJavascript(
@ -2505,7 +2504,7 @@ class InAppWebViewController {
///- Web ([Official API - Document.documentElement.scrollWidth](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollWidth)) ///- Web ([Official API - Document.documentElement.scrollWidth](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollWidth))
Future<int?> getContentWidth() async { Future<int?> getContentWidth() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
var height = await _channel?.invokeMethod('getContentWidth', args); var height = await channel?.invokeMethod('getContentWidth', args);
if (height == null || height == 0) { if (height == null || height == 0) {
// try to use javascript // try to use javascript
var scrollHeight = await evaluateJavascript( var scrollHeight = await evaluateJavascript(
@ -2539,7 +2538,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('zoomFactor', () => zoomFactor); args.putIfAbsent('zoomFactor', () => zoomFactor);
args.putIfAbsent('animated', () => iosAnimated ?? animated); args.putIfAbsent('animated', () => iosAnimated ?? animated);
return await _channel?.invokeMethod('zoomBy', args); return await channel?.invokeMethod('zoomBy', args);
} }
///Gets the URL that was originally requested for the current page. ///Gets the URL that was originally requested for the current page.
@ -2555,7 +2554,7 @@ class InAppWebViewController {
///- Web ///- Web
Future<WebUri?> getOriginalUrl() async { Future<WebUri?> getOriginalUrl() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
String? url = await _channel?.invokeMethod('getOriginalUrl', args); String? url = await channel?.invokeMethod<String?>('getOriginalUrl', args);
return url != null ? WebUri(url) : null; return url != null ? WebUri(url) : null;
} }
@ -2566,7 +2565,7 @@ class InAppWebViewController {
///- iOS ([Official API - UIScrollView.zoomScale](https://developer.apple.com/documentation/uikit/uiscrollview/1619419-zoomscale)) ///- iOS ([Official API - UIScrollView.zoomScale](https://developer.apple.com/documentation/uikit/uiscrollview/1619419-zoomscale))
Future<double?> getZoomScale() async { Future<double?> getZoomScale() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('getZoomScale', args); return await channel?.invokeMethod<double?>('getZoomScale', args);
} }
///Use [getZoomScale] instead. ///Use [getZoomScale] instead.
@ -2590,7 +2589,7 @@ class InAppWebViewController {
///- Web ///- Web
Future<String?> getSelectedText() async { Future<String?> getSelectedText() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('getSelectedText', args); return await channel?.invokeMethod<String?>('getSelectedText', args);
} }
///Gets the hit result for hitting an HTML elements. ///Gets the hit result for hitting an HTML elements.
@ -2603,7 +2602,7 @@ class InAppWebViewController {
Future<InAppWebViewHitTestResult?> getHitTestResult() async { Future<InAppWebViewHitTestResult?> getHitTestResult() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
Map<dynamic, dynamic>? hitTestResultMap = Map<dynamic, dynamic>? hitTestResultMap =
await _channel?.invokeMethod('getHitTestResult', args); await channel?.invokeMethod('getHitTestResult', args);
if (hitTestResultMap == null) { if (hitTestResultMap == null) {
return null; return null;
@ -2625,7 +2624,7 @@ class InAppWebViewController {
///- iOS ([Official API - UIResponder.resignFirstResponder](https://developer.apple.com/documentation/uikit/uiresponder/1621097-resignfirstresponder)) ///- iOS ([Official API - UIResponder.resignFirstResponder](https://developer.apple.com/documentation/uikit/uiresponder/1621097-resignfirstresponder))
Future<void> clearFocus() async { Future<void> clearFocus() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('clearFocus', args); return await channel?.invokeMethod('clearFocus', args);
} }
///Sets or updates the WebView context menu to be used next time it will appear. ///Sets or updates the WebView context menu to be used next time it will appear.
@ -2636,7 +2635,7 @@ class InAppWebViewController {
Future<void> setContextMenu(ContextMenu? contextMenu) async { Future<void> setContextMenu(ContextMenu? contextMenu) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("contextMenu", () => contextMenu?.toMap()); args.putIfAbsent("contextMenu", () => contextMenu?.toMap());
await _channel?.invokeMethod('setContextMenu', args); await channel?.invokeMethod('setContextMenu', args);
_inAppBrowser?.contextMenu = contextMenu; _inAppBrowser?.contextMenu = contextMenu;
} }
@ -2650,7 +2649,7 @@ class InAppWebViewController {
Future<RequestFocusNodeHrefResult?> requestFocusNodeHref() async { Future<RequestFocusNodeHrefResult?> requestFocusNodeHref() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
Map<dynamic, dynamic>? result = Map<dynamic, dynamic>? result =
await _channel?.invokeMethod('requestFocusNodeHref', args); await channel?.invokeMethod('requestFocusNodeHref', args);
return result != null return result != null
? RequestFocusNodeHrefResult( ? RequestFocusNodeHrefResult(
url: result['url'] != null ? WebUri(result['url']) : null, url: result['url'] != null ? WebUri(result['url']) : null,
@ -2670,7 +2669,7 @@ class InAppWebViewController {
Future<RequestImageRefResult?> requestImageRef() async { Future<RequestImageRefResult?> requestImageRef() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
Map<dynamic, dynamic>? result = Map<dynamic, dynamic>? result =
await _channel?.invokeMethod('requestImageRef', args); await channel?.invokeMethod('requestImageRef', args);
return result != null return result != null
? RequestImageRefResult( ? RequestImageRefResult(
url: result['url'] != null ? WebUri(result['url']) : null, url: result['url'] != null ? WebUri(result['url']) : null,
@ -2766,7 +2765,7 @@ class InAppWebViewController {
try { try {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
themeColor = UtilColor.fromStringRepresentation( themeColor = UtilColor.fromStringRepresentation(
await _channel?.invokeMethod('getMetaThemeColor', args)); await channel?.invokeMethod('getMetaThemeColor', args));
return themeColor; return themeColor;
} catch (e) { } catch (e) {
// not implemented // not implemented
@ -2809,7 +2808,7 @@ class InAppWebViewController {
///- Web ([Official API - Window.scrollX](https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollX)) ///- Web ([Official API - Window.scrollX](https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollX))
Future<int?> getScrollX() async { Future<int?> getScrollX() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('getScrollX', args); return await channel?.invokeMethod<int?>('getScrollX', args);
} }
///Returns the scrolled top position of the current WebView. ///Returns the scrolled top position of the current WebView.
@ -2825,7 +2824,7 @@ class InAppWebViewController {
///- Web ([Official API - Window.scrollY](https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY)) ///- Web ([Official API - Window.scrollY](https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY))
Future<int?> getScrollY() async { Future<int?> getScrollY() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('getScrollY', args); return await channel?.invokeMethod<int?>('getScrollY', args);
} }
///Gets the SSL certificate for the main top-level page or null if there is no certificate (the site is not secure). ///Gets the SSL certificate for the main top-level page or null if there is no certificate (the site is not secure).
@ -2837,7 +2836,7 @@ class InAppWebViewController {
Future<SslCertificate?> getCertificate() async { Future<SslCertificate?> getCertificate() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
Map<String, dynamic>? sslCertificateMap = Map<String, dynamic>? sslCertificateMap =
(await _channel?.invokeMethod('getCertificate', args)) (await channel?.invokeMethod('getCertificate', args))
?.cast<String, dynamic>(); ?.cast<String, dynamic>();
return SslCertificate.fromMap(sslCertificateMap); return SslCertificate.fromMap(sslCertificateMap);
} }
@ -2860,7 +2859,7 @@ class InAppWebViewController {
if (!(_userScripts[userScript.injectionTime]?.contains(userScript) ?? if (!(_userScripts[userScript.injectionTime]?.contains(userScript) ??
false)) { false)) {
_userScripts[userScript.injectionTime]?.add(userScript); _userScripts[userScript.injectionTime]?.add(userScript);
await _channel?.invokeMethod('addUserScript', args); await channel?.invokeMethod('addUserScript', args);
} }
} }
@ -2906,7 +2905,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('userScript', () => userScript.toMap()); args.putIfAbsent('userScript', () => userScript.toMap());
args.putIfAbsent('index', () => index); args.putIfAbsent('index', () => index);
await _channel?.invokeMethod('removeUserScript', args); await channel?.invokeMethod('removeUserScript', args);
return true; return true;
} }
@ -2943,7 +2942,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('groupName', () => groupName); args.putIfAbsent('groupName', () => groupName);
await _channel?.invokeMethod('removeUserScriptsByGroupName', args); await channel?.invokeMethod('removeUserScriptsByGroupName', args);
} }
///Removes the [userScripts] from the webpages content. ///Removes the [userScripts] from the webpages content.
@ -2983,7 +2982,7 @@ class InAppWebViewController {
_userScripts[UserScriptInjectionTime.AT_DOCUMENT_END]?.clear(); _userScripts[UserScriptInjectionTime.AT_DOCUMENT_END]?.clear();
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('removeAllUserScripts', args); await channel?.invokeMethod('removeAllUserScripts', args);
} }
///Returns `true` if the [userScript] has been already added, otherwise `false`. ///Returns `true` if the [userScript] has been already added, otherwise `false`.
@ -3038,7 +3037,7 @@ class InAppWebViewController {
args.putIfAbsent('functionBody', () => functionBody); args.putIfAbsent('functionBody', () => functionBody);
args.putIfAbsent('arguments', () => arguments); args.putIfAbsent('arguments', () => arguments);
args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); args.putIfAbsent('contentWorld', () => contentWorld?.toMap());
var data = await _channel?.invokeMethod('callAsyncJavaScript', args); var data = await channel?.invokeMethod('callAsyncJavaScript', args);
if (data == null) { if (data == null) {
return null; return null;
} }
@ -3081,7 +3080,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("filePath", () => filePath); args.putIfAbsent("filePath", () => filePath);
args.putIfAbsent("autoname", () => autoname); args.putIfAbsent("autoname", () => autoname);
return await _channel?.invokeMethod('saveWebArchive', args); return await channel?.invokeMethod<String?>('saveWebArchive', args);
} }
///Indicates whether the webpage context is capable of using features that require [secure contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts). ///Indicates whether the webpage context is capable of using features that require [secure contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).
@ -3098,7 +3097,7 @@ class InAppWebViewController {
///- Web ([Official API - Window.isSecureContext](https://developer.mozilla.org/en-US/docs/Web/API/Window/isSecureContext)) ///- Web ([Official API - Window.isSecureContext](https://developer.mozilla.org/en-US/docs/Web/API/Window/isSecureContext))
Future<bool> isSecureContext() async { Future<bool> isSecureContext() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('isSecureContext', args); return await channel?.invokeMethod<bool>('isSecureContext', args) ?? false;
} }
///Creates a message channel to communicate with JavaScript and returns the message channel with ports that represent the endpoints of this message channel. ///Creates a message channel to communicate with JavaScript and returns the message channel with ports that represent the endpoints of this message channel.
@ -3121,9 +3120,13 @@ class InAppWebViewController {
Future<WebMessageChannel?> createWebMessageChannel() async { Future<WebMessageChannel?> createWebMessageChannel() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
Map<String, dynamic>? result = Map<String, dynamic>? result =
(await _channel?.invokeMethod('createWebMessageChannel', args)) (await channel?.invokeMethod('createWebMessageChannel', args))
?.cast<String, dynamic>(); ?.cast<String, dynamic>();
return WebMessageChannel.fromMap(result); final webMessageChannel = WebMessageChannel.fromMap(result);
if (webMessageChannel != null) {
_webMessageChannels.add(webMessageChannel);
}
return webMessageChannel;
} }
///Post a message to main frame. The embedded application can restrict the messages to a certain target origin. ///Post a message to main frame. The embedded application can restrict the messages to a certain target origin.
@ -3149,7 +3152,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('message', () => message.toMap()); args.putIfAbsent('message', () => message.toMap());
args.putIfAbsent('targetOrigin', () => targetOrigin.toString()); args.putIfAbsent('targetOrigin', () => targetOrigin.toString());
await _channel?.invokeMethod('postWebMessage', args); await channel?.invokeMethod('postWebMessage', args);
} }
///Adds a [WebMessageListener] to the WebView and injects a JavaScript object into each frame that the [WebMessageListener] will listen on. ///Adds a [WebMessageListener] to the WebView and injects a JavaScript object into each frame that the [WebMessageListener] will listen on.
@ -3318,14 +3321,17 @@ class InAppWebViewController {
///- MacOS ///- MacOS
Future<void> addWebMessageListener( Future<void> addWebMessageListener(
WebMessageListener webMessageListener) async { WebMessageListener webMessageListener) async {
assert(!_webMessageListeners.contains(webMessageListener),
"${webMessageListener} was already added.");
assert( assert(
!_webMessageListenerObjNames.contains(webMessageListener.jsObjectName), !_webMessageListenerObjNames.contains(webMessageListener.jsObjectName),
"jsObjectName ${webMessageListener.jsObjectName} was already added."); "jsObjectName ${webMessageListener.jsObjectName} was already added.");
_webMessageListeners.add(webMessageListener);
_webMessageListenerObjNames.add(webMessageListener.jsObjectName); _webMessageListenerObjNames.add(webMessageListener.jsObjectName);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('webMessageListener', () => webMessageListener.toMap()); args.putIfAbsent('webMessageListener', () => webMessageListener.toMap());
await _channel?.invokeMethod('addWebMessageListener', args); await channel?.invokeMethod('addWebMessageListener', args);
} }
///Returns `true` if the [webMessageListener] has been already added, otherwise `false`. ///Returns `true` if the [webMessageListener] has been already added, otherwise `false`.
@ -3335,8 +3341,8 @@ class InAppWebViewController {
///- iOS ///- iOS
///- MacOS ///- MacOS
bool hasWebMessageListener(WebMessageListener webMessageListener) { bool hasWebMessageListener(WebMessageListener webMessageListener) {
return _webMessageListenerObjNames return _webMessageListeners.contains(webMessageListener) ||
.contains(webMessageListener.jsObjectName); _webMessageListenerObjNames.contains(webMessageListener.jsObjectName);
} }
///Returns `true` if the webpage can scroll vertically, otherwise `false`. ///Returns `true` if the webpage can scroll vertically, otherwise `false`.
@ -3352,7 +3358,8 @@ class InAppWebViewController {
///- Web ///- Web
Future<bool> canScrollVertically() async { Future<bool> canScrollVertically() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('canScrollVertically', args); return await channel?.invokeMethod<bool>('canScrollVertically', args) ??
false;
} }
///Returns `true` if the webpage can scroll horizontally, otherwise `false`. ///Returns `true` if the webpage can scroll horizontally, otherwise `false`.
@ -3368,7 +3375,8 @@ class InAppWebViewController {
///- Web ///- Web
Future<bool> canScrollHorizontally() async { Future<bool> canScrollHorizontally() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('canScrollHorizontally', args); return await channel?.invokeMethod<bool>('canScrollHorizontally', args) ??
false;
} }
///Starts Safe Browsing initialization. ///Starts Safe Browsing initialization.
@ -3385,7 +3393,8 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebView.startSafeBrowsing](https://developer.android.com/reference/android/webkit/WebView#startSafeBrowsing(android.content.Context,%20android.webkit.ValueCallback%3Cjava.lang.Boolean%3E))) ///- Android native WebView ([Official API - WebView.startSafeBrowsing](https://developer.android.com/reference/android/webkit/WebView#startSafeBrowsing(android.content.Context,%20android.webkit.ValueCallback%3Cjava.lang.Boolean%3E)))
Future<bool> startSafeBrowsing() async { Future<bool> startSafeBrowsing() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('startSafeBrowsing', args); return await channel?.invokeMethod<bool>('startSafeBrowsing', args) ??
false;
} }
///Clears the SSL preferences table stored in response to proceeding with SSL certificate errors. ///Clears the SSL preferences table stored in response to proceeding with SSL certificate errors.
@ -3394,7 +3403,7 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebView.clearSslPreferences](https://developer.android.com/reference/android/webkit/WebView#clearSslPreferences())) ///- Android native WebView ([Official API - WebView.clearSslPreferences](https://developer.android.com/reference/android/webkit/WebView#clearSslPreferences()))
Future<void> clearSslPreferences() async { Future<void> clearSslPreferences() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('clearSslPreferences', args); await channel?.invokeMethod('clearSslPreferences', args);
} }
///Does a best-effort attempt to pause any processing that can be paused safely, such as animations and geolocation. Note that this call does not pause JavaScript. ///Does a best-effort attempt to pause any processing that can be paused safely, such as animations and geolocation. Note that this call does not pause JavaScript.
@ -3404,7 +3413,7 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebView.onPause](https://developer.android.com/reference/android/webkit/WebView#onPause())) ///- Android native WebView ([Official API - WebView.onPause](https://developer.android.com/reference/android/webkit/WebView#onPause()))
Future<void> pause() async { Future<void> pause() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('pause', args); await channel?.invokeMethod('pause', args);
} }
///Resumes a WebView after a previous call to [pause]. ///Resumes a WebView after a previous call to [pause].
@ -3413,7 +3422,7 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebView.onResume](https://developer.android.com/reference/android/webkit/WebView#onResume())) ///- Android native WebView ([Official API - WebView.onResume](https://developer.android.com/reference/android/webkit/WebView#onResume()))
Future<void> resume() async { Future<void> resume() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('resume', args); await channel?.invokeMethod('resume', args);
} }
///Scrolls the contents of this WebView down by half the page size. ///Scrolls the contents of this WebView down by half the page size.
@ -3426,7 +3435,7 @@ class InAppWebViewController {
Future<bool> pageDown({required bool bottom}) async { Future<bool> pageDown({required bool bottom}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("bottom", () => bottom); args.putIfAbsent("bottom", () => bottom);
return await _channel?.invokeMethod('pageDown', args); return await channel?.invokeMethod<bool>('pageDown', args) ?? false;
} }
///Scrolls the contents of this WebView up by half the view size. ///Scrolls the contents of this WebView up by half the view size.
@ -3439,7 +3448,7 @@ class InAppWebViewController {
Future<bool> pageUp({required bool top}) async { Future<bool> pageUp({required bool top}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("top", () => top); args.putIfAbsent("top", () => top);
return await _channel?.invokeMethod('pageUp', args); return await channel?.invokeMethod<bool>('pageUp', args) ?? false;
} }
///Performs zoom in in this WebView. ///Performs zoom in in this WebView.
@ -3449,7 +3458,7 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebView.zoomIn](https://developer.android.com/reference/android/webkit/WebView#zoomIn())) ///- Android native WebView ([Official API - WebView.zoomIn](https://developer.android.com/reference/android/webkit/WebView#zoomIn()))
Future<bool> zoomIn() async { Future<bool> zoomIn() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('zoomIn', args); return await channel?.invokeMethod<bool>('zoomIn', args) ?? false;
} }
///Performs zoom out in this WebView. ///Performs zoom out in this WebView.
@ -3459,7 +3468,7 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebView.zoomOut](https://developer.android.com/reference/android/webkit/WebView#zoomOut())) ///- Android native WebView ([Official API - WebView.zoomOut](https://developer.android.com/reference/android/webkit/WebView#zoomOut()))
Future<bool> zoomOut() async { Future<bool> zoomOut() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('zoomOut', args); return await channel?.invokeMethod<bool>('zoomOut', args) ?? false;
} }
///Clears the internal back/forward list. ///Clears the internal back/forward list.
@ -3468,7 +3477,7 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebView.clearHistory](https://developer.android.com/reference/android/webkit/WebView#clearHistory())) ///- Android native WebView ([Official API - WebView.clearHistory](https://developer.android.com/reference/android/webkit/WebView#clearHistory()))
Future<void> clearHistory() async { Future<void> clearHistory() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('clearHistory', args); return await channel?.invokeMethod('clearHistory', args);
} }
///Reloads the current page, performing end-to-end revalidation using cache-validating conditionals if possible. ///Reloads the current page, performing end-to-end revalidation using cache-validating conditionals if possible.
@ -3478,7 +3487,7 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.reloadFromOrigin](https://developer.apple.com/documentation/webkit/wkwebview/1414956-reloadfromorigin)) ///- MacOS ([Official API - WKWebView.reloadFromOrigin](https://developer.apple.com/documentation/webkit/wkwebview/1414956-reloadfromorigin))
Future<void> reloadFromOrigin() async { Future<void> reloadFromOrigin() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('reloadFromOrigin', args); await channel?.invokeMethod('reloadFromOrigin', args);
} }
///Generates PDF data from the web views contents asynchronously. ///Generates PDF data from the web views contents asynchronously.
@ -3495,13 +3504,13 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.createPdf](https://developer.apple.com/documentation/webkit/wkwebview/3650490-createpdf)) ///- MacOS ([Official API - WKWebView.createPdf](https://developer.apple.com/documentation/webkit/wkwebview/3650490-createpdf))
Future<Uint8List?> createPdf( Future<Uint8List?> createPdf(
{@Deprecated("Use pdfConfiguration instead") {@Deprecated("Use pdfConfiguration instead")
// ignore: deprecated_member_use_from_same_package // ignore: deprecated_member_use_from_same_package
IOSWKPDFConfiguration? iosWKPdfConfiguration, IOSWKPDFConfiguration? iosWKPdfConfiguration,
PDFConfiguration? pdfConfiguration}) async { PDFConfiguration? pdfConfiguration}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('pdfConfiguration', args.putIfAbsent('pdfConfiguration',
() => pdfConfiguration?.toMap() ?? iosWKPdfConfiguration?.toMap()); () => pdfConfiguration?.toMap() ?? iosWKPdfConfiguration?.toMap());
return await _channel?.invokeMethod('createPdf', args); return await channel?.invokeMethod<Uint8List?>('createPdf', args);
} }
///Creates a web archive of the web views current contents asynchronously. ///Creates a web archive of the web views current contents asynchronously.
@ -3516,7 +3525,7 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.createWebArchiveData](https://developer.apple.com/documentation/webkit/wkwebview/3650491-createwebarchivedata)) ///- MacOS ([Official API - WKWebView.createWebArchiveData](https://developer.apple.com/documentation/webkit/wkwebview/3650491-createwebarchivedata))
Future<Uint8List?> createWebArchiveData() async { Future<Uint8List?> createWebArchiveData() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('createWebArchiveData', args); return await channel?.invokeMethod('createWebArchiveData', args);
} }
///A Boolean value indicating whether all resources on the page have been loaded over securely encrypted connections. ///A Boolean value indicating whether all resources on the page have been loaded over securely encrypted connections.
@ -3526,7 +3535,8 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.hasOnlySecureContent](https://developer.apple.com/documentation/webkit/wkwebview/1415002-hasonlysecurecontent)) ///- MacOS ([Official API - WKWebView.hasOnlySecureContent](https://developer.apple.com/documentation/webkit/wkwebview/1415002-hasonlysecurecontent))
Future<bool> hasOnlySecureContent() async { Future<bool> hasOnlySecureContent() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('hasOnlySecureContent', args); return await channel?.invokeMethod<bool>('hasOnlySecureContent', args) ??
false;
} }
///Pauses playback of all media in the web view. ///Pauses playback of all media in the web view.
@ -3540,7 +3550,7 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.pauseAllMediaPlayback](https://developer.apple.com/documentation/webkit/wkwebview/3752240-pauseallmediaplayback)). ///- MacOS ([Official API - WKWebView.pauseAllMediaPlayback](https://developer.apple.com/documentation/webkit/wkwebview/3752240-pauseallmediaplayback)).
Future<void> pauseAllMediaPlayback() async { Future<void> pauseAllMediaPlayback() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('pauseAllMediaPlayback', args); return await channel?.invokeMethod('pauseAllMediaPlayback', args);
} }
///Changes whether the webpage is suspending playback of all media in the page. ///Changes whether the webpage is suspending playback of all media in the page.
@ -3558,7 +3568,7 @@ class InAppWebViewController {
Future<void> setAllMediaPlaybackSuspended({required bool suspended}) async { Future<void> setAllMediaPlaybackSuspended({required bool suspended}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("suspended", () => suspended); args.putIfAbsent("suspended", () => suspended);
return await _channel?.invokeMethod('setAllMediaPlaybackSuspended', args); return await channel?.invokeMethod('setAllMediaPlaybackSuspended', args);
} }
///Closes all media the web view is presenting, including picture-in-picture video and fullscreen video. ///Closes all media the web view is presenting, including picture-in-picture video and fullscreen video.
@ -3572,7 +3582,7 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.closeAllMediaPresentations](https://developer.apple.com/documentation/webkit/wkwebview/3752235-closeallmediapresentations)). ///- MacOS ([Official API - WKWebView.closeAllMediaPresentations](https://developer.apple.com/documentation/webkit/wkwebview/3752235-closeallmediapresentations)).
Future<void> closeAllMediaPresentations() async { Future<void> closeAllMediaPresentations() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('closeAllMediaPresentations', args); return await channel?.invokeMethod('closeAllMediaPresentations', args);
} }
///Requests the playback status of media in the web view. ///Requests the playback status of media in the web view.
@ -3589,7 +3599,7 @@ class InAppWebViewController {
Future<MediaPlaybackState?> requestMediaPlaybackState() async { Future<MediaPlaybackState?> requestMediaPlaybackState() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return MediaPlaybackState.fromNativeValue( return MediaPlaybackState.fromNativeValue(
await _channel?.invokeMethod('requestMediaPlaybackState', args)); await channel?.invokeMethod('requestMediaPlaybackState', args));
} }
///Returns `true` if the [WebView] is in fullscreen mode, otherwise `false`. ///Returns `true` if the [WebView] is in fullscreen mode, otherwise `false`.
@ -3600,7 +3610,7 @@ class InAppWebViewController {
///- MacOS ///- MacOS
Future<bool> isInFullscreen() async { Future<bool> isInFullscreen() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('isInFullscreen', args); return await channel?.invokeMethod<bool>('isInFullscreen', args) ?? false;
} }
///Returns a [MediaCaptureState] that indicates whether the webpage is using the camera to capture images or video. ///Returns a [MediaCaptureState] that indicates whether the webpage is using the camera to capture images or video.
@ -3615,7 +3625,7 @@ class InAppWebViewController {
Future<MediaCaptureState?> getCameraCaptureState() async { Future<MediaCaptureState?> getCameraCaptureState() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return MediaCaptureState.fromNativeValue( return MediaCaptureState.fromNativeValue(
await _channel?.invokeMethod('getCameraCaptureState', args)); await channel?.invokeMethod('getCameraCaptureState', args));
} }
///Changes whether the webpage is using the camera to capture images or video. ///Changes whether the webpage is using the camera to capture images or video.
@ -3630,7 +3640,7 @@ class InAppWebViewController {
Future<void> setCameraCaptureState({required MediaCaptureState state}) async { Future<void> setCameraCaptureState({required MediaCaptureState state}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('state', () => state.toNativeValue()); args.putIfAbsent('state', () => state.toNativeValue());
await _channel?.invokeMethod('setCameraCaptureState', args); await channel?.invokeMethod('setCameraCaptureState', args);
} }
///Returns a [MediaCaptureState] that indicates whether the webpage is using the microphone to capture audio. ///Returns a [MediaCaptureState] that indicates whether the webpage is using the microphone to capture audio.
@ -3645,7 +3655,7 @@ class InAppWebViewController {
Future<MediaCaptureState?> getMicrophoneCaptureState() async { Future<MediaCaptureState?> getMicrophoneCaptureState() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return MediaCaptureState.fromNativeValue( return MediaCaptureState.fromNativeValue(
await _channel?.invokeMethod('getMicrophoneCaptureState', args)); await channel?.invokeMethod('getMicrophoneCaptureState', args));
} }
///Changes whether the webpage is using the microphone to capture audio. ///Changes whether the webpage is using the microphone to capture audio.
@ -3661,7 +3671,7 @@ class InAppWebViewController {
{required MediaCaptureState state}) async { {required MediaCaptureState state}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('state', () => state.toNativeValue()); args.putIfAbsent('state', () => state.toNativeValue());
await _channel?.invokeMethod('setMicrophoneCaptureState', args); await channel?.invokeMethod('setMicrophoneCaptureState', args);
} }
///Loads the web content from the data you provide as if the data were the response to the request. ///Loads the web content from the data you provide as if the data were the response to the request.
@ -3697,7 +3707,7 @@ class InAppWebViewController {
args.putIfAbsent('urlRequest', () => urlRequest.toMap()); args.putIfAbsent('urlRequest', () => urlRequest.toMap());
args.putIfAbsent('data', () => data); args.putIfAbsent('data', () => data);
args.putIfAbsent('urlResponse', () => urlResponse?.toMap()); args.putIfAbsent('urlResponse', () => urlResponse?.toMap());
await _channel?.invokeMethod('loadSimulatedRequest', args); await channel?.invokeMethod('loadSimulatedRequest', args);
} }
///Returns the iframe `id` attribute used on the Web platform. ///Returns the iframe `id` attribute used on the Web platform.
@ -3706,7 +3716,7 @@ class InAppWebViewController {
///- Web ///- Web
Future<String?> getIFrameId() async { Future<String?> getIFrameId() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('getIFrameId', args); return await channel?.invokeMethod<String?>('getIFrameId', args);
} }
///Gets the default user agent. ///Gets the default user agent.
@ -3717,7 +3727,9 @@ class InAppWebViewController {
///- MacOS ///- MacOS
static Future<String> getDefaultUserAgent() async { static Future<String> getDefaultUserAgent() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _staticChannel.invokeMethod('getDefaultUserAgent', args); return await _staticChannel.invokeMethod<String>(
'getDefaultUserAgent', args) ??
'';
} }
///Clears the client certificate preferences stored in response to proceeding/cancelling client cert requests. ///Clears the client certificate preferences stored in response to proceeding/cancelling client cert requests.
@ -3777,7 +3789,9 @@ class InAppWebViewController {
{required List<String> hosts}) async { {required List<String> hosts}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('hosts', () => hosts); args.putIfAbsent('hosts', () => hosts);
return await _staticChannel.invokeMethod('setSafeBrowsingAllowlist', args); return await _staticChannel.invokeMethod<bool>(
'setSafeBrowsingAllowlist', args) ??
false;
} }
///If WebView has already been loaded into the current process this method will return the package that was used to load it. ///If WebView has already been loaded into the current process this method will return the package that was used to load it.
@ -3833,7 +3847,8 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebViewCompat.getVariationsHeader](https://developer.android.com/reference/androidx/webkit/WebViewCompat#getVariationsHeader())) ///- Android native WebView ([Official API - WebViewCompat.getVariationsHeader](https://developer.android.com/reference/androidx/webkit/WebViewCompat#getVariationsHeader()))
static Future<String?> getVariationsHeader() async { static Future<String?> getVariationsHeader() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _staticChannel.invokeMethod('getVariationsHeader', args); return await _staticChannel.invokeMethod<String?>(
'getVariationsHeader', args);
} }
///Returns `true` if WebView is running in multi process mode. ///Returns `true` if WebView is running in multi process mode.
@ -3850,7 +3865,9 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebViewCompat.isMultiProcessEnabled](https://developer.android.com/reference/androidx/webkit/WebViewCompat#isMultiProcessEnabled())) ///- Android native WebView ([Official API - WebViewCompat.isMultiProcessEnabled](https://developer.android.com/reference/androidx/webkit/WebViewCompat#isMultiProcessEnabled()))
static Future<bool> isMultiProcessEnabled() async { static Future<bool> isMultiProcessEnabled() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _staticChannel.invokeMethod('isMultiProcessEnabled', args); return await _staticChannel.invokeMethod<bool>(
'isMultiProcessEnabled', args) ??
false;
} }
///Returns a Boolean value that indicates whether WebKit natively supports resources with the specified URL scheme. ///Returns a Boolean value that indicates whether WebKit natively supports resources with the specified URL scheme.
@ -3901,24 +3918,17 @@ class InAppWebViewController {
static Future<String> get tRexRunnerCss async => await rootBundle.loadString( static Future<String> get tRexRunnerCss async => await rootBundle.loadString(
'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.css'); 'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.css');
///Used internally. ///View ID used internally.
MethodChannel? getChannel() {
return _channel;
}
///Used internally.
dynamic getViewId() { dynamic getViewId() {
return _id; return _id;
} }
///Disposes the controller. ///Disposes the controller.
@override
void dispose({bool isKeepAlive = false}) { void dispose({bool isKeepAlive = false}) {
if (!isKeepAlive) { disposeChannel(removeMethodCallHandler: !isKeepAlive);
_channel?.setMethodCallHandler(null);
}
android.dispose(); android.dispose();
ios.dispose(); ios.dispose();
_channel = null;
_webview = null; _webview = null;
_inAppBrowser = null; _inAppBrowser = null;
webStorage.dispose(); webStorage.dispose();
@ -3927,6 +3937,18 @@ class InAppWebViewController {
_userScripts.clear(); _userScripts.clear();
_webMessageListenerObjNames.clear(); _webMessageListenerObjNames.clear();
_injectedScriptsFromURL.clear(); _injectedScriptsFromURL.clear();
for (final webMessageChannel in _webMessageChannels) {
webMessageChannel.dispose();
}
_webMessageChannels.clear();
for (final webMessageListener in _webMessageListeners) {
webMessageListener.dispose();
}
_webMessageListeners.clear();
} }
} }
} }
extension InternalInAppWebViewController on InAppWebViewController {
get handleMethod => _handleMethod;
}

View File

@ -1,5 +1,7 @@
import '../types/main.dart'; import '../types/main.dart';
import '../util.dart'; import '../util.dart';
import '../web_message/web_message_channel.dart';
import '../web_message/web_message_listener.dart';
import 'in_app_webview.dart'; import 'in_app_webview.dart';
import 'in_app_webview_controller.dart'; import 'in_app_webview_controller.dart';
@ -24,10 +26,14 @@ class InAppWebViewControllerKeepAliveProps {
Map<UserScriptInjectionTime, List<UserScript>> userScripts; Map<UserScriptInjectionTime, List<UserScript>> userScripts;
Set<String> webMessageListenerObjNames; Set<String> webMessageListenerObjNames;
Map<String, ScriptHtmlTagAttributes> injectedScriptsFromURL; Map<String, ScriptHtmlTagAttributes> injectedScriptsFromURL;
Set<WebMessageChannel> webMessageChannels = Set();
Set<WebMessageListener> webMessageListeners = Set();
InAppWebViewControllerKeepAliveProps( InAppWebViewControllerKeepAliveProps(
{required this.javaScriptHandlersMap, {required this.javaScriptHandlersMap,
required this.userScripts, required this.userScripts,
required this.webMessageListenerObjNames, required this.webMessageListenerObjNames,
required this.injectedScriptsFromURL}); required this.injectedScriptsFromURL,
required this.webMessageChannels,
required this.webMessageListeners});
} }

View File

@ -1,6 +1,6 @@
export 'webview.dart'; export 'webview.dart';
export 'in_app_webview.dart'; export 'in_app_webview.dart';
export 'in_app_webview_controller.dart'; export 'in_app_webview_controller.dart' hide InternalInAppWebViewController;
export 'in_app_webview_settings.dart' export 'in_app_webview_settings.dart'
show show
InAppWebViewSettings, InAppWebViewSettings,

View File

@ -1,7 +1,7 @@
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/src/util.dart';
import '../types/print_job_info.dart'; import '../types/print_job_info.dart';
import '../in_app_webview/in_app_webview_controller.dart'; import '../in_app_webview/in_app_webview_controller.dart';
import '../types/disposable.dart';
///A completion handler for the [PrintJobController]. ///A completion handler for the [PrintJobController].
typedef PrintJobCompletionHandler = Future<void> Function( typedef PrintJobCompletionHandler = Future<void> Function(
@ -13,12 +13,10 @@ typedef PrintJobCompletionHandler = Future<void> Function(
///- Android native WebView ///- Android native WebView
///- iOS ///- iOS
///- MacOS ///- MacOS
class PrintJobController implements Disposable { class PrintJobController extends ChannelController {
///Print job ID. ///Print job ID.
final String id; final String id;
MethodChannel? _channel;
///A completion handler used to handle the conclusion of the print job (for instance, to reset state) and to handle any errors encountered in printing. ///A completion handler used to handle the conclusion of the print job (for instance, to reset state) and to handle any errors encountered in printing.
/// ///
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
@ -27,16 +25,10 @@ class PrintJobController implements Disposable {
PrintJobCompletionHandler onComplete; PrintJobCompletionHandler onComplete;
PrintJobController({required this.id}) { PrintJobController({required this.id}) {
this._channel = MethodChannel( channel = MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_printjobcontroller_$id'); 'com.pichillilorenzo/flutter_inappwebview_printjobcontroller_$id');
this._channel?.setMethodCallHandler((call) async { handler = _handleMethod;
try { initMethodCallHandler();
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
} }
Future<dynamic> _handleMethod(MethodCall call) async { Future<dynamic> _handleMethod(MethodCall call) async {
@ -60,7 +52,7 @@ class PrintJobController implements Disposable {
///- Android native WebView ([Official API - PrintJob.cancel](https://developer.android.com/reference/android/print/PrintJob#cancel())) ///- Android native WebView ([Official API - PrintJob.cancel](https://developer.android.com/reference/android/print/PrintJob#cancel()))
Future<void> cancel() async { Future<void> cancel() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('cancel', args); await channel?.invokeMethod('cancel', args);
} }
///Restarts this print job. ///Restarts this print job.
@ -70,7 +62,7 @@ class PrintJobController implements Disposable {
///- Android native WebView ([Official API - PrintJob.restart](https://developer.android.com/reference/android/print/PrintJob#restart())) ///- Android native WebView ([Official API - PrintJob.restart](https://developer.android.com/reference/android/print/PrintJob#restart()))
Future<void> restart() async { Future<void> restart() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('restart', args); await channel?.invokeMethod('restart', args);
} }
///Dismisses the printing-options sheet or popover. ///Dismisses the printing-options sheet or popover.
@ -85,7 +77,7 @@ class PrintJobController implements Disposable {
Future<void> dismiss({bool animated: true}) async { Future<void> dismiss({bool animated: true}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("animated", () => animated); args.putIfAbsent("animated", () => animated);
await _channel?.invokeMethod('dismiss', args); await channel?.invokeMethod('dismiss', args);
} }
///Gets the [PrintJobInfo] that describes this job. ///Gets the [PrintJobInfo] that describes this job.
@ -101,7 +93,7 @@ class PrintJobController implements Disposable {
Future<PrintJobInfo?> getInfo() async { Future<PrintJobInfo?> getInfo() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
Map<String, dynamic>? infoMap = Map<String, dynamic>? infoMap =
(await _channel?.invokeMethod('getInfo', args)) (await channel?.invokeMethod('getInfo', args))
?.cast<String, dynamic>(); ?.cast<String, dynamic>();
return PrintJobInfo.fromMap(infoMap); return PrintJobInfo.fromMap(infoMap);
} }
@ -115,8 +107,7 @@ class PrintJobController implements Disposable {
@override @override
Future<void> dispose() async { Future<void> dispose() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('dispose', args); await channel?.invokeMethod('dispose', args);
_channel?.setMethodCallHandler(null); disposeChannel();
_channel = null;
} }
} }

View File

@ -20,12 +20,11 @@ import '../debug_logging_settings.dart';
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- Android native WebView ///- Android native WebView
///- iOS ///- iOS
class PullToRefreshController { class PullToRefreshController extends ChannelController {
@Deprecated("Use settings instead") @Deprecated("Use settings instead")
// ignore: deprecated_member_use_from_same_package // ignore: deprecated_member_use_from_same_package
late PullToRefreshOptions options; late PullToRefreshOptions options;
late PullToRefreshSettings settings; late PullToRefreshSettings settings;
MethodChannel? _channel;
///Debug settings. ///Debug settings.
static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings(); static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings();
@ -73,7 +72,7 @@ class PullToRefreshController {
Future<void> setEnabled(bool enabled) async { Future<void> setEnabled(bool enabled) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('enabled', () => enabled); args.putIfAbsent('enabled', () => enabled);
await _channel?.invokeMethod('setEnabled', args); await channel?.invokeMethod('setEnabled', args);
} }
///Returns `true` is pull-to-refresh feature is enabled, otherwise `false`. ///Returns `true` is pull-to-refresh feature is enabled, otherwise `false`.
@ -83,13 +82,13 @@ class PullToRefreshController {
///- iOS ([Official API - UIScrollView.refreshControl](https://developer.apple.com/documentation/uikit/uiscrollview/2127691-refreshcontrol)) ///- iOS ([Official API - UIScrollView.refreshControl](https://developer.apple.com/documentation/uikit/uiscrollview/2127691-refreshcontrol))
Future<bool> isEnabled() async { Future<bool> isEnabled() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('isEnabled', args); return await channel?.invokeMethod<bool>('isEnabled', args) ?? false;
} }
Future<void> _setRefreshing(bool refreshing) async { Future<void> _setRefreshing(bool refreshing) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('refreshing', () => refreshing); args.putIfAbsent('refreshing', () => refreshing);
await _channel?.invokeMethod('setRefreshing', args); await channel?.invokeMethod('setRefreshing', args);
} }
///Tells the controller that a refresh operation was started programmatically. ///Tells the controller that a refresh operation was started programmatically.
@ -126,7 +125,7 @@ class PullToRefreshController {
///- iOS ([Official API - UIRefreshControl.isRefreshing](https://developer.apple.com/documentation/uikit/uirefreshcontrol/1624844-isrefreshing)) ///- iOS ([Official API - UIRefreshControl.isRefreshing](https://developer.apple.com/documentation/uikit/uirefreshcontrol/1624844-isrefreshing))
Future<bool> isRefreshing() async { Future<bool> isRefreshing() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('isRefreshing', args); return await channel?.invokeMethod<bool>('isRefreshing', args) ?? false;
} }
///Sets the color of the refresh control. ///Sets the color of the refresh control.
@ -137,7 +136,7 @@ class PullToRefreshController {
Future<void> setColor(Color color) async { Future<void> setColor(Color color) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('color', () => color.toHex()); args.putIfAbsent('color', () => color.toHex());
await _channel?.invokeMethod('setColor', args); await channel?.invokeMethod('setColor', args);
} }
///Sets the background color of the refresh control. ///Sets the background color of the refresh control.
@ -148,7 +147,7 @@ class PullToRefreshController {
Future<void> setBackgroundColor(Color color) async { Future<void> setBackgroundColor(Color color) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('color', () => color.toHex()); args.putIfAbsent('color', () => color.toHex());
await _channel?.invokeMethod('setBackgroundColor', args); await channel?.invokeMethod('setBackgroundColor', args);
} }
///Set the distance to trigger a sync in dips. ///Set the distance to trigger a sync in dips.
@ -158,7 +157,7 @@ class PullToRefreshController {
Future<void> setDistanceToTriggerSync(int distanceToTriggerSync) async { Future<void> setDistanceToTriggerSync(int distanceToTriggerSync) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('distanceToTriggerSync', () => distanceToTriggerSync); args.putIfAbsent('distanceToTriggerSync', () => distanceToTriggerSync);
await _channel?.invokeMethod('setDistanceToTriggerSync', args); await channel?.invokeMethod('setDistanceToTriggerSync', args);
} }
///Sets the distance that the refresh indicator can be pulled beyond its resting position during a swipe gesture. ///Sets the distance that the refresh indicator can be pulled beyond its resting position during a swipe gesture.
@ -168,7 +167,7 @@ class PullToRefreshController {
Future<void> setSlingshotDistance(int slingshotDistance) async { Future<void> setSlingshotDistance(int slingshotDistance) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('slingshotDistance', () => slingshotDistance); args.putIfAbsent('slingshotDistance', () => slingshotDistance);
await _channel?.invokeMethod('setSlingshotDistance', args); await channel?.invokeMethod('setSlingshotDistance', args);
} }
///Gets the default distance that the refresh indicator can be pulled beyond its resting position during a swipe gesture. ///Gets the default distance that the refresh indicator can be pulled beyond its resting position during a swipe gesture.
@ -177,7 +176,7 @@ class PullToRefreshController {
///- Android native WebView ([Official API - SwipeRefreshLayout.DEFAULT_SLINGSHOT_DISTANCE](https://developer.android.com/reference/androidx/swiperefreshlayout/widget/SwipeRefreshLayout#DEFAULT_SLINGSHOT_DISTANCE())) ///- Android native WebView ([Official API - SwipeRefreshLayout.DEFAULT_SLINGSHOT_DISTANCE](https://developer.android.com/reference/androidx/swiperefreshlayout/widget/SwipeRefreshLayout#DEFAULT_SLINGSHOT_DISTANCE()))
Future<int> getDefaultSlingshotDistance() async { Future<int> getDefaultSlingshotDistance() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('getDefaultSlingshotDistance', args); return await channel?.invokeMethod<int>('getDefaultSlingshotDistance', args) ?? 0;
} }
///Use [setIndicatorSize] instead. ///Use [setIndicatorSize] instead.
@ -185,7 +184,7 @@ class PullToRefreshController {
Future<void> setSize(AndroidPullToRefreshSize size) async { Future<void> setSize(AndroidPullToRefreshSize size) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('size', () => size.toNativeValue()); args.putIfAbsent('size', () => size.toNativeValue());
await _channel?.invokeMethod('setSize', args); await channel?.invokeMethod('setSize', args);
} }
///Sets the size of the refresh indicator. One of [PullToRefreshSize.DEFAULT], or [PullToRefreshSize.LARGE]. ///Sets the size of the refresh indicator. One of [PullToRefreshSize.DEFAULT], or [PullToRefreshSize.LARGE].
@ -195,7 +194,7 @@ class PullToRefreshController {
Future<void> setIndicatorSize(PullToRefreshSize size) async { Future<void> setIndicatorSize(PullToRefreshSize size) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('size', () => size.toNativeValue()); args.putIfAbsent('size', () => size.toNativeValue());
await _channel?.invokeMethod('setSize', args); await channel?.invokeMethod('setSize', args);
} }
///Use [setStyledTitle] instead. ///Use [setStyledTitle] instead.
@ -203,7 +202,7 @@ class PullToRefreshController {
Future<void> setAttributedTitle(IOSNSAttributedString attributedTitle) async { Future<void> setAttributedTitle(IOSNSAttributedString attributedTitle) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('attributedTitle', () => attributedTitle.toMap()); args.putIfAbsent('attributedTitle', () => attributedTitle.toMap());
await _channel?.invokeMethod('setStyledTitle', args); await channel?.invokeMethod('setStyledTitle', args);
} }
///Sets the styled title text to display in the refresh control. ///Sets the styled title text to display in the refresh control.
@ -213,30 +212,21 @@ class PullToRefreshController {
Future<void> setStyledTitle(AttributedString attributedTitle) async { Future<void> setStyledTitle(AttributedString attributedTitle) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('attributedTitle', () => attributedTitle.toMap()); args.putIfAbsent('attributedTitle', () => attributedTitle.toMap());
await _channel?.invokeMethod('setStyledTitle', args); await channel?.invokeMethod('setStyledTitle', args);
} }
///Disposes the controller. ///Disposes the controller.
@override
void dispose({bool isKeepAlive = false}) { void dispose({bool isKeepAlive = false}) {
if (!isKeepAlive) { disposeChannel(removeMethodCallHandler: !isKeepAlive);
_channel?.setMethodCallHandler(null);
}
_channel = null;
} }
} }
extension InternalPullToRefreshController on PullToRefreshController { extension InternalPullToRefreshController on PullToRefreshController {
void init(dynamic id) { void init(dynamic id) {
this._channel = MethodChannel( channel = MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_$id'); 'com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_$id');
this._channel?.setMethodCallHandler((call) async { handler = _handleMethod;
if (_channel == null) return null; initMethodCallHandler();
try {
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
} }
} }

View File

@ -4,8 +4,10 @@ import 'dart:typed_data';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'debug_logging_settings.dart'; import 'debug_logging_settings.dart';
import 'types/disposable.dart';
class Util { class Util {
static bool get isWeb => kIsWeb; static bool get isWeb => kIsWeb;
@ -582,3 +584,60 @@ void debugLog(
class Color_ extends Color { class Color_ extends Color {
Color_(int value) : super(value); Color_(int value) : super(value);
} }
abstract class ChannelController implements Disposable {
MethodChannel? _channel;
Future<dynamic> Function(MethodCall call)? _handler;
static bool debugAssertNotDisposed(ChannelController controller) {
assert(() {
if (controller.disposed) {
throw FlutterError(
'A ${controller.runtimeType} was used after being disposed.\n'
'Once the ${controller.runtimeType} has been disposed, it '
'can no longer be used.',
);
}
return true;
}());
return true;
}
}
extension InternalChannelController on ChannelController {
set channel (MethodChannel? channel) => _channel = channel;
MethodChannel? get channel {
assert(ChannelController.debugAssertNotDisposed(this));
return this._channel;
}
set handler (Future<dynamic> Function(MethodCall call)? handler) => _handler = handler;
Future<dynamic> Function(MethodCall call)? get handler => _handler;
bool get disposed => _channel == null;
initMethodCallHandler() {
assert(channel != null, 'Method Channel for ${runtimeType} not initialized!');
assert(handler != null, 'Method Call Handler for ${runtimeType} not initialized!');
channel?.setMethodCallHandler((call) async {
if (disposed) return null;
try {
return await handler!(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
}
disposeChannel({bool removeMethodCallHandler = true}) {
if (removeMethodCallHandler) {
channel?.setMethodCallHandler(null);
}
channel = null;
handler = null;
}
}

View File

@ -5,13 +5,11 @@ import 'package:flutter/services.dart';
import 'headless_inappwebview_manager.dart'; import 'headless_inappwebview_manager.dart';
import 'in_app_web_view_web_element.dart'; import 'in_app_web_view_web_element.dart';
import '../util.dart'; import '../util.dart';
import '../types/disposable.dart';
class HeadlessInAppWebViewWebElement implements Disposable { class HeadlessInAppWebViewWebElement extends ChannelController {
String id; String id;
late BinaryMessenger _messenger; late BinaryMessenger _messenger;
InAppWebViewWebElement? webView; InAppWebViewWebElement? webView;
late MethodChannel? _channel;
HeadlessInAppWebViewWebElement( HeadlessInAppWebViewWebElement(
{required this.id, {required this.id,
@ -19,23 +17,16 @@ class HeadlessInAppWebViewWebElement implements Disposable {
required this.webView}) { required this.webView}) {
this._messenger = messenger; this._messenger = messenger;
_channel = MethodChannel( channel = MethodChannel(
'com.pichillilorenzo/flutter_headless_inappwebview_${this.id}', 'com.pichillilorenzo/flutter_headless_inappwebview_${this.id}',
const StandardMethodCodec(), const StandardMethodCodec(),
_messenger, _messenger,
); );
handler = _handleMethod;
this._channel?.setMethodCallHandler((call) async { initMethodCallHandler();
try {
return await handleMethodCall(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
} }
Future<dynamic> handleMethodCall(MethodCall call) async { Future<dynamic> _handleMethod(MethodCall call) async {
switch (call.method) { switch (call.method) {
case "dispose": case "dispose":
dispose(); dispose();
@ -57,7 +48,7 @@ class HeadlessInAppWebViewWebElement implements Disposable {
} }
void onWebViewCreated() async { void onWebViewCreated() async {
await _channel?.invokeMethod("onWebViewCreated"); await channel?.invokeMethod("onWebViewCreated");
} }
void setSize(Size size) { void setSize(Size size) {
@ -73,8 +64,7 @@ class HeadlessInAppWebViewWebElement implements Disposable {
@override @override
void dispose() { void dispose() {
_channel?.setMethodCallHandler(null); disposeChannel();
_channel = null;
HeadlessInAppWebViewManager.webViews.putIfAbsent(id, () => null); HeadlessInAppWebViewManager.webViews.putIfAbsent(id, () => null);
webView?.dispose(); webView?.dispose();
webView = null; webView = null;

View File

@ -1,35 +1,27 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/src/util.dart';
import 'dart:js' as js; import 'dart:js' as js;
import 'web_platform_manager.dart'; import 'web_platform_manager.dart';
import '../types/disposable.dart';
class PlatformUtil implements Disposable { class PlatformUtil extends ChannelController {
late BinaryMessenger _messenger; late BinaryMessenger _messenger;
late MethodChannel? _channel;
PlatformUtil({required BinaryMessenger messenger}) { PlatformUtil({required BinaryMessenger messenger}) {
this._messenger = messenger; this._messenger = messenger;
_channel = MethodChannel( channel = MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_platformutil', 'com.pichillilorenzo/flutter_inappwebview_platformutil',
const StandardMethodCodec(), const StandardMethodCodec(),
_messenger, _messenger,
); );
handler = _handleMethod;
this._channel?.setMethodCallHandler((call) async { initMethodCallHandler();
try {
return await handleMethodCall(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
} }
Future<dynamic> handleMethodCall(MethodCall call) async { Future<dynamic> _handleMethod(MethodCall call) async {
switch (call.method) { switch (call.method) {
case "getWebCookieExpirationDate": case "getWebCookieExpirationDate":
int timestamp = call.arguments['date']; int timestamp = call.arguments['date'];
@ -51,7 +43,6 @@ class PlatformUtil implements Disposable {
@override @override
void dispose() { void dispose() {
_channel?.setMethodCallHandler(null); disposeChannel();
_channel = null;
} }
} }

View File

@ -1,11 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import '../util.dart'; import '../util.dart';
import '../debug_logging_settings.dart'; import '../debug_logging_settings.dart';
import '../types/main.dart'; import '../types/main.dart';
import '../types/disposable.dart';
import '../web_uri.dart'; import '../web_uri.dart';
import 'web_authenticate_session_settings.dart'; import 'web_authenticate_session_settings.dart';
@ -37,7 +35,7 @@ typedef WebAuthenticationSessionCompletionHandler = Future<void> Function(
///**Supported Platforms/Implementations**: ///**Supported Platforms/Implementations**:
///- iOS ///- iOS
///- MacOS ///- MacOS
class WebAuthenticationSession implements Disposable { class WebAuthenticationSession extends ChannelController {
///Debug settings. ///Debug settings.
static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings(); static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings();
@ -56,7 +54,6 @@ class WebAuthenticationSession implements Disposable {
///A completion handler the session calls when it completes successfully, or when the user cancels the session. ///A completion handler the session calls when it completes successfully, or when the user cancels the session.
WebAuthenticationSessionCompletionHandler onComplete; WebAuthenticationSessionCompletionHandler onComplete;
MethodChannel? _channel;
static const MethodChannel _sharedChannel = const MethodChannel( static const MethodChannel _sharedChannel = const MethodChannel(
'com.pichillilorenzo/flutter_webauthenticationsession'); 'com.pichillilorenzo/flutter_webauthenticationsession');
@ -102,16 +99,10 @@ class WebAuthenticationSession implements Disposable {
id = IdGenerator.generate(); id = IdGenerator.generate();
this.initialSettings = this.initialSettings =
initialSettings ?? WebAuthenticationSessionSettings(); initialSettings ?? WebAuthenticationSessionSettings();
this._channel = MethodChannel( channel = MethodChannel(
'com.pichillilorenzo/flutter_webauthenticationsession_$id'); 'com.pichillilorenzo/flutter_webauthenticationsession_$id');
this._channel?.setMethodCallHandler((call) async { handler = _handleMethod;
try { initMethodCallHandler();
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
} }
_debugLog(String method, dynamic args) { _debugLog(String method, dynamic args) {
@ -147,7 +138,7 @@ class WebAuthenticationSession implements Disposable {
///- iOS ([Official API - ASWebAuthenticationSession.canStart](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession/3516277-canstart)) ///- iOS ([Official API - ASWebAuthenticationSession.canStart](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession/3516277-canstart))
Future<bool> canStart() async { Future<bool> canStart() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('canStart', args); return await channel?.invokeMethod<bool>('canStart', args) ?? false;
} }
///Starts the web authentication session. ///Starts the web authentication session.
@ -161,7 +152,7 @@ class WebAuthenticationSession implements Disposable {
///- iOS ([Official API - ASWebAuthenticationSession.start](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession/2990953-start)) ///- iOS ([Official API - ASWebAuthenticationSession.start](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession/2990953-start))
Future<bool> start() async { Future<bool> start() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('start', args); return await channel?.invokeMethod<bool>('start', args) ?? false;
} }
///Cancels the web authentication session. ///Cancels the web authentication session.
@ -173,7 +164,7 @@ class WebAuthenticationSession implements Disposable {
///- iOS ([Official API - ASWebAuthenticationSession.cancel](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession/2990951-cancel)) ///- iOS ([Official API - ASWebAuthenticationSession.cancel](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession/2990951-cancel))
Future<void> cancel() async { Future<void> cancel() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod("cancel", args); await channel?.invokeMethod("cancel", args);
} }
///Disposes the web authentication session. ///Disposes the web authentication session.
@ -183,9 +174,8 @@ class WebAuthenticationSession implements Disposable {
@override @override
Future<void> dispose() async { Future<void> dispose() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod("dispose", args); await channel?.invokeMethod("dispose", args);
_channel?.setMethodCallHandler(null); disposeChannel();
_channel = null;
} }
///Returns `true` if [ASWebAuthenticationSession](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession) ///Returns `true` if [ASWebAuthenticationSession](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession)
@ -196,6 +186,6 @@ class WebAuthenticationSession implements Disposable {
///- iOS ///- iOS
static Future<bool> isAvailable() async { static Future<bool> isAvailable() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
return await _sharedChannel.invokeMethod("isAvailable", args); return await _sharedChannel.invokeMethod<bool>("isAvailable", args) ?? false;
} }
} }

View File

@ -1,4 +1,5 @@
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/src/util.dart';
import 'web_message_port.dart'; import 'web_message_port.dart';
///The representation of the [HTML5 message channels](https://html.spec.whatwg.org/multipage/web-messaging.html#message-channels). ///The representation of the [HTML5 message channels](https://html.spec.whatwg.org/multipage/web-messaging.html#message-channels).
@ -7,7 +8,7 @@ import 'web_message_port.dart';
///- Android native WebView ///- Android native WebView
///- iOS ///- iOS
///- MacOS ///- MacOS
class WebMessageChannel { class WebMessageChannel extends ChannelController {
///Message Channel ID used internally. ///Message Channel ID used internally.
final String id; final String id;
@ -17,20 +18,12 @@ class WebMessageChannel {
///The second [WebMessagePort] object of the channel. ///The second [WebMessagePort] object of the channel.
final WebMessagePort port2; final WebMessagePort port2;
MethodChannel? _channel;
WebMessageChannel( WebMessageChannel(
{required this.id, required this.port1, required this.port2}) { {required this.id, required this.port1, required this.port2}) {
this._channel = MethodChannel( channel = MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_web_message_channel_$id'); 'com.pichillilorenzo/flutter_inappwebview_web_message_channel_$id');
this._channel?.setMethodCallHandler((call) async { handler = _handleMethod;
try { initMethodCallHandler();
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
} }
static WebMessageChannel? fromMap(Map<String, dynamic>? map) { static WebMessageChannel? fromMap(Map<String, dynamic>? map) {
@ -62,6 +55,12 @@ class WebMessageChannel {
return null; return null;
} }
///Disposes the web message channel.
@override
void dispose() {
disposeChannel();
}
@override @override
String toString() { String toString() {
return 'WebMessageChannel{id: $id, port1: $port1, port2: $port2}'; return 'WebMessageChannel{id: $id, port1: $port1, port2: $port2}';
@ -69,5 +68,5 @@ class WebMessageChannel {
} }
extension InternalWebMessageChannel on WebMessageChannel { extension InternalWebMessageChannel on WebMessageChannel {
MethodChannel? get channel => _channel; MethodChannel? get internalChannel => channel;
} }

View File

@ -10,7 +10,7 @@ import '../web_uri.dart';
///- Android native WebView ///- Android native WebView
///- iOS ///- iOS
///- MacOS ///- MacOS
class WebMessageListener { class WebMessageListener extends ChannelController {
///Message Listener ID used internally. ///Message Listener ID used internally.
late final String id; late final String id;
@ -34,8 +34,6 @@ class WebMessageListener {
///**Official Android API**: https://developer.android.com/reference/androidx/webkit/WebViewCompat.WebMessageListener#onPostMessage(android.webkit.WebView,%20androidx.webkit.WebMessageCompat,%20android.net.Uri,%20boolean,%20androidx.webkit.JavaScriptReplyProxy) ///**Official Android API**: https://developer.android.com/reference/androidx/webkit/WebViewCompat.WebMessageListener#onPostMessage(android.webkit.WebView,%20androidx.webkit.WebMessageCompat,%20android.net.Uri,%20boolean,%20androidx.webkit.JavaScriptReplyProxy)
OnPostMessageCallback? onPostMessage; OnPostMessageCallback? onPostMessage;
MethodChannel? _channel;
WebMessageListener( WebMessageListener(
{required this.jsObjectName, {required this.jsObjectName,
Set<String>? allowedOriginRules, Set<String>? allowedOriginRules,
@ -45,16 +43,10 @@ class WebMessageListener {
allowedOriginRules != null ? allowedOriginRules : Set.from(["*"]); allowedOriginRules != null ? allowedOriginRules : Set.from(["*"]);
assert(!this.allowedOriginRules.contains(""), assert(!this.allowedOriginRules.contains(""),
"allowedOriginRules cannot contain empty strings"); "allowedOriginRules cannot contain empty strings");
this._channel = MethodChannel( channel= MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_web_message_listener_${id}_$jsObjectName'); 'com.pichillilorenzo/flutter_inappwebview_web_message_listener_${id}_$jsObjectName');
this._channel?.setMethodCallHandler((call) async { handler = _handleMethod;
try { initMethodCallHandler();
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
} }
Future<dynamic> _handleMethod(MethodCall call) async { Future<dynamic> _handleMethod(MethodCall call) async {
@ -78,6 +70,11 @@ class WebMessageListener {
return null; return null;
} }
@override
void dispose() {
disposeChannel();
}
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return { return {
"id": id, "id": id,
@ -114,7 +111,7 @@ class JavaScriptReplyProxy {
Future<void> postMessage(String message) async { Future<void> postMessage(String message) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('message', () => message); args.putIfAbsent('message', () => message);
await _webMessageListener._channel?.invokeMethod('postMessage', args); await _webMessageListener.channel?.invokeMethod('postMessage', args);
} }
@override @override

View File

@ -37,7 +37,7 @@ class WebMessagePort {
Future<void> setWebMessageCallback(WebMessageCallback? onMessage) async { Future<void> setWebMessageCallback(WebMessageCallback? onMessage) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('index', () => this._index); args.putIfAbsent('index', () => this._index);
await _webMessageChannel.channel await _webMessageChannel.internalChannel
?.invokeMethod('setWebMessageCallback', args); ?.invokeMethod('setWebMessageCallback', args);
this._onMessage = onMessage; this._onMessage = onMessage;
} }
@ -47,14 +47,14 @@ class WebMessagePort {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('index', () => this._index); args.putIfAbsent('index', () => this._index);
args.putIfAbsent('message', () => message.toMap()); args.putIfAbsent('message', () => message.toMap());
await _webMessageChannel.channel?.invokeMethod('postMessage', args); await _webMessageChannel.internalChannel?.invokeMethod('postMessage', args);
} }
///Close the message port and free any resources associated with it. ///Close the message port and free any resources associated with it.
Future<void> close() async { Future<void> close() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('index', () => this._index); args.putIfAbsent('index', () => this._index);
await _webMessageChannel.channel?.invokeMethod('close', args); await _webMessageChannel.internalChannel?.invokeMethod('close', args);
} }
@ExchangeableObjectMethod(toMapMergeWith: true) @ExchangeableObjectMethod(toMapMergeWith: true)

View File

@ -1,6 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import '../in_app_webview/in_app_webview_controller.dart'; import '../in_app_webview/in_app_webview_controller.dart';
import '../types/disposable.dart';
import '../types/main.dart'; import '../types/main.dart';
import 'web_storage_item.dart'; import 'web_storage_item.dart';
@ -12,7 +13,7 @@ import 'web_storage_item.dart';
///- iOS ///- iOS
///- MacOS ///- MacOS
///- Web ///- Web
class WebStorage { class WebStorage implements Disposable {
///Represents `window.localStorage`. ///Represents `window.localStorage`.
LocalStorage localStorage; LocalStorage localStorage;
@ -22,6 +23,7 @@ class WebStorage {
WebStorage({required this.localStorage, required this.sessionStorage}); WebStorage({required this.localStorage, required this.sessionStorage});
///Disposes the web storage. ///Disposes the web storage.
@override
void dispose() { void dispose() {
localStorage.dispose(); localStorage.dispose();
sessionStorage.dispose(); sessionStorage.dispose();
@ -30,7 +32,7 @@ class WebStorage {
///Class that provides methods to manage the JavaScript [Storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage) object. ///Class that provides methods to manage the JavaScript [Storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage) object.
///It is used by [LocalStorage] and [SessionStorage]. ///It is used by [LocalStorage] and [SessionStorage].
class Storage { class Storage implements Disposable {
InAppWebViewController? _controller; InAppWebViewController? _controller;
///The web storage type: `window.sessionStorage` or `window.localStorage`. ///The web storage type: `window.sessionStorage` or `window.localStorage`.
@ -179,6 +181,7 @@ class Storage {
} }
///Disposes the storage. ///Disposes the storage.
@override
void dispose() { void dispose() {
_controller = null; _controller = null;
} }