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
- 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 "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))

View File

@ -4,7 +4,6 @@ import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_i
import '../types/proxy_rule.dart';
import 'webview_feature.dart';
import '../in_app_webview/webview.dart';
import '../types/main.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 'chrome_safari_browser.dart';
import 'chrome_safari_browser_menu_item.dart';
import '../web_uri.dart';
part 'chrome_safari_action_button.g.dart';

View File

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

View File

@ -8,9 +8,7 @@ import '../util.dart';
///- Android native WebView
///- iOS
///- MacOS
class FindInteractionController {
MethodChannel? _channel;
class FindInteractionController extends ChannelController {
///Debug settings.
static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings();
@ -81,7 +79,7 @@ class FindInteractionController {
Future<void> findAll({String? find}) async {
Map<String, dynamic> args = <String, dynamic>{};
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.
@ -99,7 +97,7 @@ class FindInteractionController {
Future<void> findNext({bool forward = true}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('forward', () => forward);
await _channel?.invokeMethod('findNext', args);
await channel?.invokeMethod('findNext', args);
}
///Clears the highlighting surrounding text matches created by [findAll].
@ -114,7 +112,7 @@ class FindInteractionController {
///- MacOS
Future<void> clearMatches() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('clearMatches', args);
await channel?.invokeMethod('clearMatches', args);
}
///Pre-populate the search text to be used.
@ -129,7 +127,7 @@ class FindInteractionController {
Future<void> setSearchText(String? searchText) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('searchText', () => searchText);
await _channel?.invokeMethod('setSearchText', args);
await channel?.invokeMethod('setSearchText', args);
}
///Get the search text used.
@ -143,7 +141,7 @@ class FindInteractionController {
///- MacOS
Future<String?> getSearchText() async {
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.
@ -154,7 +152,7 @@ class FindInteractionController {
///- iOS ([Official API - UIFindInteraction.isFindNavigatorVisible](https://developer.apple.com/documentation/uikit/uifindinteraction/3975828-isfindnavigatorvisible?changes=_2))
Future<bool?> isFindNavigatorVisible() async {
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.
@ -165,7 +163,7 @@ class FindInteractionController {
///- iOS ([Official API - UIFindInteraction.updateResultCount](https://developer.apple.com/documentation/uikit/uifindinteraction/3975835-updateresultcount?changes=_2))
Future<void> updateResultCount() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('updateResultCount', args);
await channel?.invokeMethod('updateResultCount', args);
}
///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))
Future<void> presentFindNavigator() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('presentFindNavigator', args);
await channel?.invokeMethod('presentFindNavigator', args);
}
///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))
Future<void> dismissFindNavigator() async {
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.
@ -199,33 +197,23 @@ class FindInteractionController {
Future<FindSession?> getActiveFindSession() async {
Map<String, dynamic> args = <String, dynamic>{};
Map<String, dynamic>? result =
(await _channel?.invokeMethod('getActiveFindSession', args))
(await channel?.invokeMethod('getActiveFindSession', args))
?.cast<String, dynamic>();
return FindSession.fromMap(result);
}
///Disposes the controller.
@override
void dispose({bool isKeepAlive = false}) {
if (!isKeepAlive) {
_channel?.setMethodCallHandler(null);
}
_channel = null;
disposeChannel(removeMethodCallHandler: !isKeepAlive);
}
}
extension InternalFindInteractionController on FindInteractionController {
void init(dynamic id) {
this._channel = MethodChannel(
channel = MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_find_interaction_$id');
this._channel?.setMethodCallHandler((call) async {
if (_channel == null) return null;
try {
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
handler = _handleMethod;
initMethodCallHandler();
}
}

View File

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

View File

@ -29,7 +29,7 @@ import '../types/disposable.dart';
///- Web
///- MacOS
///{@endtemplate}
class HeadlessInAppWebView implements WebView, Disposable {
class HeadlessInAppWebView extends ChannelController implements WebView, Disposable {
///View ID.
late final String id;
@ -38,7 +38,6 @@ class HeadlessInAppWebView implements WebView, Disposable {
static const MethodChannel _sharedChannel =
const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview');
MethodChannel? _channel;
InAppWebViewController? _webViewController;
@ -187,19 +186,13 @@ class HeadlessInAppWebView implements WebView, Disposable {
_webViewController = InAppWebViewController(id, this);
pullToRefreshController?.init(id);
findInteractionController?.init(id);
this._channel =
channel =
MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview_$id');
this._channel?.setMethodCallHandler((call) async {
try {
return await handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
handler = _handleMethod;
initMethodCallHandler();
}
Future<dynamic> handleMethod(MethodCall call) async {
Future<dynamic> _handleMethod(MethodCall call) async {
switch (call.method) {
case "onWebViewCreated":
if (onWebViewCreated != null && _webViewController != null) {
@ -310,9 +303,8 @@ class HeadlessInAppWebView implements WebView, Disposable {
return;
}
Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('dispose', args);
_channel?.setMethodCallHandler(null);
_channel = null;
await channel?.invokeMethod('dispose', args);
disposeChannel();
_started = false;
_running = false;
_webViewController?.dispose();
@ -353,7 +345,7 @@ class HeadlessInAppWebView implements WebView, Disposable {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('size', () => size.toMap());
await _channel?.invokeMethod('setSize', args);
await channel?.invokeMethod('setSize', args);
}
///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> sizeMap =
(await _channel?.invokeMethod('getSize', args))
(await channel?.invokeMethod('getSize', args))
?.cast<String, dynamic>();
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]
///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;
MethodChannel? _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 =
HashMap<String, JavaScriptHandlerCallback>();
Map<UserScriptInjectionTime, List<UserScript>> _userScripts = {
@ -64,6 +63,8 @@ class InAppWebViewController {
};
Set<String> _webMessageListenerObjNames = Set();
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 final Map<InAppWebViewKeepAlive, InAppWebViewControllerKeepAliveProps?>
@ -86,17 +87,10 @@ class InAppWebViewController {
InAppWebViewController(dynamic id, WebView webview) {
this._id = id;
this._channel =
MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id');
this._channel?.setMethodCallHandler((call) async {
if (_channel == null) return null;
try {
return await handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
channel = MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id');
handler = handleMethod;
initMethodCallHandler();
this._webview = webview;
final initialUserScripts = webview.initialUserScripts;
@ -122,7 +116,7 @@ class InAppWebViewController {
MethodChannel channel,
InAppBrowser inAppBrowser,
UnmodifiableListView<UserScript>? initialUserScripts) {
this._channel = channel;
this.channel = channel;
this._inAppBrowser = inAppBrowser;
if (initialUserScripts != null) {
@ -143,8 +137,8 @@ class InAppWebViewController {
}
void _init() {
android = AndroidInAppWebViewController(channel: _channel!);
ios = IOSInAppWebViewController(channel: _channel!);
android = AndroidInAppWebViewController(channel: channel!);
ios = IOSInAppWebViewController(channel: channel!);
webStorage = WebStorage(
localStorage: LocalStorage(this), sessionStorage: SessionStorage(this));
@ -158,13 +152,17 @@ class InAppWebViewController {
injectedScriptsFromURL: _injectedScriptsFromURL,
javaScriptHandlersMap: _javaScriptHandlersMap,
userScripts: _userScripts,
webMessageListenerObjNames: _webMessageListenerObjNames);
webMessageListenerObjNames: _webMessageListenerObjNames,
webMessageChannels: _webMessageChannels,
webMessageListeners: _webMessageListeners);
} else {
// restore controller properties
_injectedScriptsFromURL = props.injectedScriptsFromURL;
_javaScriptHandlersMap = props.javaScriptHandlersMap;
_userScripts = props.userScripts;
_webMessageListenerObjNames = props.webMessageListenerObjNames;
_webMessageChannels = props.webMessageChannels;
_webMessageListeners = props.webMessageListeners;
}
}
}
@ -180,7 +178,7 @@ class InAppWebViewController {
args: args);
}
Future<dynamic> handleMethod(MethodCall call) async {
Future<dynamic> _handleMethod(MethodCall call) async {
if (WebView.debugLoggingSettings.enabled &&
call.method != "onCallJsHandler") {
_debugLog(call.method, call.arguments);
@ -1430,7 +1428,7 @@ class InAppWebViewController {
///- Web
Future<WebUri?> getUrl() async {
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;
}
@ -1445,7 +1443,7 @@ class InAppWebViewController {
///- Web
Future<String?> getTitle() async {
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.
@ -1456,7 +1454,7 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.estimatedProgress](https://developer.apple.com/documentation/webkit/wkwebview/1415007-estimatedprogress))
Future<int?> getProgress() async {
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.
@ -1704,7 +1702,7 @@ class InAppWebViewController {
Future<void> loadUrl(
{required URLRequest urlRequest,
@Deprecated('Use allowingReadAccessTo instead')
Uri? iosAllowingReadAccessTo,
Uri? iosAllowingReadAccessTo,
WebUri? allowingReadAccessTo}) async {
assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty);
assert(
@ -1719,7 +1717,7 @@ class InAppWebViewController {
() =>
allowingReadAccessTo?.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.
@ -1743,7 +1741,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url.toString());
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.
@ -1770,11 +1768,10 @@ class InAppWebViewController {
String mimeType = "text/html",
String encoding = "utf8",
WebUri? baseUrl,
@Deprecated('Use historyUrl instead')
Uri? androidHistoryUrl,
@Deprecated('Use historyUrl instead') Uri? androidHistoryUrl,
WebUri? historyUrl,
@Deprecated('Use allowingReadAccessTo instead')
Uri? iosAllowingReadAccessTo,
Uri? iosAllowingReadAccessTo,
WebUri? allowingReadAccessTo}) async {
assert(
allowingReadAccessTo == null || allowingReadAccessTo.isScheme("file"));
@ -1797,7 +1794,7 @@ class InAppWebViewController {
() =>
allowingReadAccessTo?.toString() ??
iosAllowingReadAccessTo?.toString());
await _channel?.invokeMethod('loadData', args);
await channel?.invokeMethod('loadData', args);
}
///Loads the given [assetFilePath].
@ -1839,7 +1836,7 @@ class InAppWebViewController {
assert(assetFilePath.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('assetFilePath', () => assetFilePath);
await _channel?.invokeMethod('loadFile', args);
await channel?.invokeMethod('loadFile', args);
}
///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))
Future<void> reload() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('reload', args);
await channel?.invokeMethod('reload', args);
}
///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))
Future<void> goBack() async {
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.
@ -1878,7 +1875,7 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.canGoBack](https://developer.apple.com/documentation/webkit/wkwebview/1414966-cangoback))
Future<bool> canGoBack() async {
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.
@ -1892,7 +1889,7 @@ class InAppWebViewController {
///- Web ([Official API - History.forward](https://developer.mozilla.org/en-US/docs/Web/API/History/forward))
Future<void> goForward() async {
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.
@ -1903,7 +1900,7 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.canGoForward](https://developer.apple.com/documentation/webkit/wkwebview/1414962-cangoforward))
Future<bool> canGoForward() async {
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.
@ -1918,7 +1915,7 @@ class InAppWebViewController {
Future<void> goBackOrForward({required int steps}) async {
Map<String, dynamic> args = <String, dynamic>{};
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.
@ -1930,7 +1927,8 @@ class InAppWebViewController {
Future<bool> canGoBackOrForward({required int steps}) async {
Map<String, dynamic> args = <String, dynamic>{};
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.
@ -1958,7 +1956,7 @@ class InAppWebViewController {
///- Web
Future<bool> isLoading() async {
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.
@ -1972,7 +1970,7 @@ class InAppWebViewController {
///- Web ([Official API - Window.stop](https://developer.mozilla.org/en-US/docs/Web/API/Window/stop))
Future<void> stopLoading() async {
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.
@ -2002,7 +2000,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('source', () => source);
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)) {
try {
// try to json decode the data coming from JavaScript
@ -2041,7 +2039,7 @@ class InAppWebViewController {
args.putIfAbsent('urlFile', () => urlFile.toString());
args.putIfAbsent(
'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.
@ -2081,7 +2079,7 @@ class InAppWebViewController {
Future<void> injectCSSCode({required String source}) async {
Map<String, dynamic> args = <String, dynamic>{};
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.
@ -2108,7 +2106,7 @@ class InAppWebViewController {
args.putIfAbsent('urlFile', () => urlFile.toString());
args.putIfAbsent(
'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.
@ -2233,7 +2231,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent(
'screenshotConfiguration', () => screenshotConfiguration?.toMap());
return await _channel?.invokeMethod('takeScreenshot', args);
return await channel?.invokeMethod<Uint8List?>('takeScreenshot', args);
}
///Use [setSettings] instead.
@ -2269,7 +2267,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{};
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.
@ -2283,7 +2281,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{};
Map<dynamic, dynamic>? settings =
await _channel?.invokeMethod('getSettings', args);
await channel?.invokeMethod('getSettings', args);
if (settings != null) {
settings = settings.cast<String, dynamic>();
return InAppWebViewSettings.fromMap(settings as Map<String, dynamic>);
@ -2304,7 +2302,7 @@ class InAppWebViewController {
Future<WebHistory?> getCopyBackForwardList() async {
Map<String, dynamic> args = <String, dynamic>{};
Map<String, dynamic>? result =
(await _channel?.invokeMethod('getCopyBackForwardList', args))
(await channel?.invokeMethod('getCopyBackForwardList', args))
?.cast<String, dynamic>();
return WebHistory.fromMap(result);
}
@ -2317,7 +2315,7 @@ class InAppWebViewController {
///- MacOS
Future<void> clearCache() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('clearCache', args);
await channel?.invokeMethod('clearCache', args);
}
///Use [FindInteractionController.findAll] instead.
@ -2325,7 +2323,7 @@ class InAppWebViewController {
Future<void> findAllAsync({required String find}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('find', () => find);
await _channel?.invokeMethod('findAll', args);
await channel?.invokeMethod('findAll', args);
}
///Use [FindInteractionController.findNext] instead.
@ -2333,14 +2331,14 @@ class InAppWebViewController {
Future<void> findNext({required bool forward}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('forward', () => forward);
await _channel?.invokeMethod('findNext', args);
await channel?.invokeMethod('findNext', args);
}
///Use [FindInteractionController.clearMatches] instead.
@Deprecated("Use FindInteractionController.clearMatches instead")
Future<void> clearMatches() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('clearMatches', args);
await channel?.invokeMethod('clearMatches', args);
}
///Use [tRexRunnerHtml] instead.
@ -2378,7 +2376,7 @@ class InAppWebViewController {
args.putIfAbsent('x', () => x);
args.putIfAbsent('y', () => y);
args.putIfAbsent('animated', () => animated);
await _channel?.invokeMethod('scrollTo', args);
await channel?.invokeMethod('scrollTo', args);
}
///Moves the scrolled position of the WebView.
@ -2404,7 +2402,7 @@ class InAppWebViewController {
args.putIfAbsent('x', () => x);
args.putIfAbsent('y', () => y);
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.
@ -2420,7 +2418,7 @@ class InAppWebViewController {
///- MacOS
Future<void> pauseTimers() async {
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.
@ -2435,7 +2433,7 @@ class InAppWebViewController {
///- MacOS
Future<void> resumeTimers() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('resumeTimers', args);
await channel?.invokeMethod('resumeTimers', args);
}
///Prints the current page.
@ -2458,7 +2456,8 @@ class InAppWebViewController {
{PrintJobSettings? settings}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("settings", () => settings?.toMap());
String? jobId = await _channel?.invokeMethod('printCurrentPage', args);
String? jobId =
await channel?.invokeMethod<String?>('printCurrentPage', args);
if (jobId != null) {
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))
Future<int?> getContentHeight() async {
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) {
// try to use javascript
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))
Future<int?> getContentWidth() async {
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) {
// try to use javascript
var scrollHeight = await evaluateJavascript(
@ -2539,7 +2538,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('zoomFactor', () => zoomFactor);
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.
@ -2555,7 +2554,7 @@ class InAppWebViewController {
///- Web
Future<WebUri?> getOriginalUrl() async {
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;
}
@ -2566,7 +2565,7 @@ class InAppWebViewController {
///- iOS ([Official API - UIScrollView.zoomScale](https://developer.apple.com/documentation/uikit/uiscrollview/1619419-zoomscale))
Future<double?> getZoomScale() async {
Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('getZoomScale', args);
return await channel?.invokeMethod<double?>('getZoomScale', args);
}
///Use [getZoomScale] instead.
@ -2590,7 +2589,7 @@ class InAppWebViewController {
///- Web
Future<String?> getSelectedText() async {
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.
@ -2603,7 +2602,7 @@ class InAppWebViewController {
Future<InAppWebViewHitTestResult?> getHitTestResult() async {
Map<String, dynamic> args = <String, dynamic>{};
Map<dynamic, dynamic>? hitTestResultMap =
await _channel?.invokeMethod('getHitTestResult', args);
await channel?.invokeMethod('getHitTestResult', args);
if (hitTestResultMap == null) {
return null;
@ -2625,7 +2624,7 @@ class InAppWebViewController {
///- iOS ([Official API - UIResponder.resignFirstResponder](https://developer.apple.com/documentation/uikit/uiresponder/1621097-resignfirstresponder))
Future<void> clearFocus() async {
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.
@ -2636,7 +2635,7 @@ class InAppWebViewController {
Future<void> setContextMenu(ContextMenu? contextMenu) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("contextMenu", () => contextMenu?.toMap());
await _channel?.invokeMethod('setContextMenu', args);
await channel?.invokeMethod('setContextMenu', args);
_inAppBrowser?.contextMenu = contextMenu;
}
@ -2650,7 +2649,7 @@ class InAppWebViewController {
Future<RequestFocusNodeHrefResult?> requestFocusNodeHref() async {
Map<String, dynamic> args = <String, dynamic>{};
Map<dynamic, dynamic>? result =
await _channel?.invokeMethod('requestFocusNodeHref', args);
await channel?.invokeMethod('requestFocusNodeHref', args);
return result != null
? RequestFocusNodeHrefResult(
url: result['url'] != null ? WebUri(result['url']) : null,
@ -2670,7 +2669,7 @@ class InAppWebViewController {
Future<RequestImageRefResult?> requestImageRef() async {
Map<String, dynamic> args = <String, dynamic>{};
Map<dynamic, dynamic>? result =
await _channel?.invokeMethod('requestImageRef', args);
await channel?.invokeMethod('requestImageRef', args);
return result != null
? RequestImageRefResult(
url: result['url'] != null ? WebUri(result['url']) : null,
@ -2766,7 +2765,7 @@ class InAppWebViewController {
try {
Map<String, dynamic> args = <String, dynamic>{};
themeColor = UtilColor.fromStringRepresentation(
await _channel?.invokeMethod('getMetaThemeColor', args));
await channel?.invokeMethod('getMetaThemeColor', args));
return themeColor;
} catch (e) {
// not implemented
@ -2809,7 +2808,7 @@ class InAppWebViewController {
///- Web ([Official API - Window.scrollX](https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollX))
Future<int?> getScrollX() async {
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.
@ -2825,7 +2824,7 @@ class InAppWebViewController {
///- Web ([Official API - Window.scrollY](https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY))
Future<int?> getScrollY() async {
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).
@ -2837,7 +2836,7 @@ class InAppWebViewController {
Future<SslCertificate?> getCertificate() async {
Map<String, dynamic> args = <String, dynamic>{};
Map<String, dynamic>? sslCertificateMap =
(await _channel?.invokeMethod('getCertificate', args))
(await channel?.invokeMethod('getCertificate', args))
?.cast<String, dynamic>();
return SslCertificate.fromMap(sslCertificateMap);
}
@ -2860,7 +2859,7 @@ class InAppWebViewController {
if (!(_userScripts[userScript.injectionTime]?.contains(userScript) ??
false)) {
_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>{};
args.putIfAbsent('userScript', () => userScript.toMap());
args.putIfAbsent('index', () => index);
await _channel?.invokeMethod('removeUserScript', args);
await channel?.invokeMethod('removeUserScript', args);
return true;
}
@ -2943,7 +2942,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('groupName', () => groupName);
await _channel?.invokeMethod('removeUserScriptsByGroupName', args);
await channel?.invokeMethod('removeUserScriptsByGroupName', args);
}
///Removes the [userScripts] from the webpages content.
@ -2983,7 +2982,7 @@ class InAppWebViewController {
_userScripts[UserScriptInjectionTime.AT_DOCUMENT_END]?.clear();
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`.
@ -3038,7 +3037,7 @@ class InAppWebViewController {
args.putIfAbsent('functionBody', () => functionBody);
args.putIfAbsent('arguments', () => arguments);
args.putIfAbsent('contentWorld', () => contentWorld?.toMap());
var data = await _channel?.invokeMethod('callAsyncJavaScript', args);
var data = await channel?.invokeMethod('callAsyncJavaScript', args);
if (data == null) {
return null;
}
@ -3081,7 +3080,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("filePath", () => filePath);
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).
@ -3098,7 +3097,7 @@ class InAppWebViewController {
///- Web ([Official API - Window.isSecureContext](https://developer.mozilla.org/en-US/docs/Web/API/Window/isSecureContext))
Future<bool> isSecureContext() async {
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.
@ -3121,9 +3120,13 @@ class InAppWebViewController {
Future<WebMessageChannel?> createWebMessageChannel() async {
Map<String, dynamic> args = <String, dynamic>{};
Map<String, dynamic>? result =
(await _channel?.invokeMethod('createWebMessageChannel', args))
(await channel?.invokeMethod('createWebMessageChannel', args))
?.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.
@ -3149,7 +3152,7 @@ class InAppWebViewController {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('message', () => message.toMap());
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.
@ -3318,14 +3321,17 @@ class InAppWebViewController {
///- MacOS
Future<void> addWebMessageListener(
WebMessageListener webMessageListener) async {
assert(!_webMessageListeners.contains(webMessageListener),
"${webMessageListener} was already added.");
assert(
!_webMessageListenerObjNames.contains(webMessageListener.jsObjectName),
"jsObjectName ${webMessageListener.jsObjectName} was already added.");
_webMessageListeners.add(webMessageListener);
_webMessageListenerObjNames.add(webMessageListener.jsObjectName);
Map<String, dynamic> args = <String, dynamic>{};
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`.
@ -3335,8 +3341,8 @@ class InAppWebViewController {
///- iOS
///- MacOS
bool hasWebMessageListener(WebMessageListener webMessageListener) {
return _webMessageListenerObjNames
.contains(webMessageListener.jsObjectName);
return _webMessageListeners.contains(webMessageListener) ||
_webMessageListenerObjNames.contains(webMessageListener.jsObjectName);
}
///Returns `true` if the webpage can scroll vertically, otherwise `false`.
@ -3352,7 +3358,8 @@ class InAppWebViewController {
///- Web
Future<bool> canScrollVertically() async {
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`.
@ -3368,7 +3375,8 @@ class InAppWebViewController {
///- Web
Future<bool> canScrollHorizontally() async {
Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('canScrollHorizontally', args);
return await channel?.invokeMethod<bool>('canScrollHorizontally', args) ??
false;
}
///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)))
Future<bool> startSafeBrowsing() async {
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.
@ -3394,7 +3403,7 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebView.clearSslPreferences](https://developer.android.com/reference/android/webkit/WebView#clearSslPreferences()))
Future<void> clearSslPreferences() async {
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.
@ -3404,7 +3413,7 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebView.onPause](https://developer.android.com/reference/android/webkit/WebView#onPause()))
Future<void> pause() async {
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].
@ -3413,7 +3422,7 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebView.onResume](https://developer.android.com/reference/android/webkit/WebView#onResume()))
Future<void> resume() async {
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.
@ -3426,7 +3435,7 @@ class InAppWebViewController {
Future<bool> pageDown({required bool bottom}) async {
Map<String, dynamic> args = <String, dynamic>{};
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.
@ -3439,7 +3448,7 @@ class InAppWebViewController {
Future<bool> pageUp({required bool top}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("top", () => top);
return await _channel?.invokeMethod('pageUp', args);
return await channel?.invokeMethod<bool>('pageUp', args) ?? false;
}
///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()))
Future<bool> zoomIn() async {
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.
@ -3459,7 +3468,7 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebView.zoomOut](https://developer.android.com/reference/android/webkit/WebView#zoomOut()))
Future<bool> zoomOut() async {
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.
@ -3468,7 +3477,7 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebView.clearHistory](https://developer.android.com/reference/android/webkit/WebView#clearHistory()))
Future<void> clearHistory() async {
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.
@ -3478,7 +3487,7 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.reloadFromOrigin](https://developer.apple.com/documentation/webkit/wkwebview/1414956-reloadfromorigin))
Future<void> reloadFromOrigin() async {
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.
@ -3495,13 +3504,13 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.createPdf](https://developer.apple.com/documentation/webkit/wkwebview/3650490-createpdf))
Future<Uint8List?> createPdf(
{@Deprecated("Use pdfConfiguration instead")
// ignore: deprecated_member_use_from_same_package
IOSWKPDFConfiguration? iosWKPdfConfiguration,
// ignore: deprecated_member_use_from_same_package
IOSWKPDFConfiguration? iosWKPdfConfiguration,
PDFConfiguration? pdfConfiguration}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('pdfConfiguration',
() => 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.
@ -3516,7 +3525,7 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.createWebArchiveData](https://developer.apple.com/documentation/webkit/wkwebview/3650491-createwebarchivedata))
Future<Uint8List?> createWebArchiveData() async {
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.
@ -3526,7 +3535,8 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.hasOnlySecureContent](https://developer.apple.com/documentation/webkit/wkwebview/1415002-hasonlysecurecontent))
Future<bool> hasOnlySecureContent() async {
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.
@ -3540,7 +3550,7 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.pauseAllMediaPlayback](https://developer.apple.com/documentation/webkit/wkwebview/3752240-pauseallmediaplayback)).
Future<void> pauseAllMediaPlayback() async {
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.
@ -3558,7 +3568,7 @@ class InAppWebViewController {
Future<void> setAllMediaPlaybackSuspended({required bool suspended}) async {
Map<String, dynamic> args = <String, dynamic>{};
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.
@ -3572,7 +3582,7 @@ class InAppWebViewController {
///- MacOS ([Official API - WKWebView.closeAllMediaPresentations](https://developer.apple.com/documentation/webkit/wkwebview/3752235-closeallmediapresentations)).
Future<void> closeAllMediaPresentations() async {
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.
@ -3589,7 +3599,7 @@ class InAppWebViewController {
Future<MediaPlaybackState?> requestMediaPlaybackState() async {
Map<String, dynamic> args = <String, dynamic>{};
return MediaPlaybackState.fromNativeValue(
await _channel?.invokeMethod('requestMediaPlaybackState', args));
await channel?.invokeMethod('requestMediaPlaybackState', args));
}
///Returns `true` if the [WebView] is in fullscreen mode, otherwise `false`.
@ -3600,7 +3610,7 @@ class InAppWebViewController {
///- MacOS
Future<bool> isInFullscreen() async {
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.
@ -3615,7 +3625,7 @@ class InAppWebViewController {
Future<MediaCaptureState?> getCameraCaptureState() async {
Map<String, dynamic> args = <String, dynamic>{};
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.
@ -3630,7 +3640,7 @@ class InAppWebViewController {
Future<void> setCameraCaptureState({required MediaCaptureState state}) async {
Map<String, dynamic> args = <String, dynamic>{};
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.
@ -3645,7 +3655,7 @@ class InAppWebViewController {
Future<MediaCaptureState?> getMicrophoneCaptureState() async {
Map<String, dynamic> args = <String, dynamic>{};
return MediaCaptureState.fromNativeValue(
await _channel?.invokeMethod('getMicrophoneCaptureState', args));
await channel?.invokeMethod('getMicrophoneCaptureState', args));
}
///Changes whether the webpage is using the microphone to capture audio.
@ -3661,7 +3671,7 @@ class InAppWebViewController {
{required MediaCaptureState state}) async {
Map<String, dynamic> args = <String, dynamic>{};
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.
@ -3697,7 +3707,7 @@ class InAppWebViewController {
args.putIfAbsent('urlRequest', () => urlRequest.toMap());
args.putIfAbsent('data', () => data);
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.
@ -3706,7 +3716,7 @@ class InAppWebViewController {
///- Web
Future<String?> getIFrameId() async {
Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('getIFrameId', args);
return await channel?.invokeMethod<String?>('getIFrameId', args);
}
///Gets the default user agent.
@ -3717,7 +3727,9 @@ class InAppWebViewController {
///- MacOS
static Future<String> getDefaultUserAgent() async {
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.
@ -3777,7 +3789,9 @@ class InAppWebViewController {
{required List<String> hosts}) async {
Map<String, dynamic> args = <String, dynamic>{};
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.
@ -3833,7 +3847,8 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebViewCompat.getVariationsHeader](https://developer.android.com/reference/androidx/webkit/WebViewCompat#getVariationsHeader()))
static Future<String?> getVariationsHeader() async {
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.
@ -3850,7 +3865,9 @@ class InAppWebViewController {
///- Android native WebView ([Official API - WebViewCompat.isMultiProcessEnabled](https://developer.android.com/reference/androidx/webkit/WebViewCompat#isMultiProcessEnabled()))
static Future<bool> isMultiProcessEnabled() async {
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.
@ -3901,24 +3918,17 @@ class InAppWebViewController {
static Future<String> get tRexRunnerCss async => await rootBundle.loadString(
'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.css');
///Used internally.
MethodChannel? getChannel() {
return _channel;
}
///Used internally.
///View ID used internally.
dynamic getViewId() {
return _id;
}
///Disposes the controller.
@override
void dispose({bool isKeepAlive = false}) {
if (!isKeepAlive) {
_channel?.setMethodCallHandler(null);
}
disposeChannel(removeMethodCallHandler: !isKeepAlive);
android.dispose();
ios.dispose();
_channel = null;
_webview = null;
_inAppBrowser = null;
webStorage.dispose();
@ -3927,6 +3937,18 @@ class InAppWebViewController {
_userScripts.clear();
_webMessageListenerObjNames.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 '../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_controller.dart';
@ -24,10 +26,14 @@ class InAppWebViewControllerKeepAliveProps {
Map<UserScriptInjectionTime, List<UserScript>> userScripts;
Set<String> webMessageListenerObjNames;
Map<String, ScriptHtmlTagAttributes> injectedScriptsFromURL;
Set<WebMessageChannel> webMessageChannels = Set();
Set<WebMessageListener> webMessageListeners = Set();
InAppWebViewControllerKeepAliveProps(
{required this.javaScriptHandlersMap,
required this.userScripts,
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 'in_app_webview.dart';
export 'in_app_webview_controller.dart';
export 'in_app_webview_controller.dart' hide InternalInAppWebViewController;
export 'in_app_webview_settings.dart'
show
InAppWebViewSettings,

View File

@ -1,7 +1,7 @@
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/src/util.dart';
import '../types/print_job_info.dart';
import '../in_app_webview/in_app_webview_controller.dart';
import '../types/disposable.dart';
///A completion handler for the [PrintJobController].
typedef PrintJobCompletionHandler = Future<void> Function(
@ -13,12 +13,10 @@ typedef PrintJobCompletionHandler = Future<void> Function(
///- Android native WebView
///- iOS
///- MacOS
class PrintJobController implements Disposable {
class PrintJobController extends ChannelController {
///Print job 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.
///
///**Supported Platforms/Implementations**:
@ -27,16 +25,10 @@ class PrintJobController implements Disposable {
PrintJobCompletionHandler onComplete;
PrintJobController({required this.id}) {
this._channel = MethodChannel(
channel = MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_printjobcontroller_$id');
this._channel?.setMethodCallHandler((call) async {
try {
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
handler = _handleMethod;
initMethodCallHandler();
}
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()))
Future<void> cancel() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('cancel', args);
await channel?.invokeMethod('cancel', args);
}
///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()))
Future<void> restart() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('restart', args);
await channel?.invokeMethod('restart', args);
}
///Dismisses the printing-options sheet or popover.
@ -85,7 +77,7 @@ class PrintJobController implements Disposable {
Future<void> dismiss({bool animated: true}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("animated", () => animated);
await _channel?.invokeMethod('dismiss', args);
await channel?.invokeMethod('dismiss', args);
}
///Gets the [PrintJobInfo] that describes this job.
@ -101,7 +93,7 @@ class PrintJobController implements Disposable {
Future<PrintJobInfo?> getInfo() async {
Map<String, dynamic> args = <String, dynamic>{};
Map<String, dynamic>? infoMap =
(await _channel?.invokeMethod('getInfo', args))
(await channel?.invokeMethod('getInfo', args))
?.cast<String, dynamic>();
return PrintJobInfo.fromMap(infoMap);
}
@ -115,8 +107,7 @@ class PrintJobController implements Disposable {
@override
Future<void> dispose() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod('dispose', args);
_channel?.setMethodCallHandler(null);
_channel = null;
await channel?.invokeMethod('dispose', args);
disposeChannel();
}
}

View File

@ -20,12 +20,11 @@ import '../debug_logging_settings.dart';
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
class PullToRefreshController {
class PullToRefreshController extends ChannelController {
@Deprecated("Use settings instead")
// ignore: deprecated_member_use_from_same_package
late PullToRefreshOptions options;
late PullToRefreshSettings settings;
MethodChannel? _channel;
///Debug settings.
static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings();
@ -73,7 +72,7 @@ class PullToRefreshController {
Future<void> setEnabled(bool enabled) async {
Map<String, dynamic> args = <String, dynamic>{};
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`.
@ -83,13 +82,13 @@ class PullToRefreshController {
///- iOS ([Official API - UIScrollView.refreshControl](https://developer.apple.com/documentation/uikit/uiscrollview/2127691-refreshcontrol))
Future<bool> isEnabled() async {
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 {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('refreshing', () => refreshing);
await _channel?.invokeMethod('setRefreshing', args);
await channel?.invokeMethod('setRefreshing', args);
}
///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))
Future<bool> isRefreshing() async {
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.
@ -137,7 +136,7 @@ class PullToRefreshController {
Future<void> setColor(Color color) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('color', () => color.toHex());
await _channel?.invokeMethod('setColor', args);
await channel?.invokeMethod('setColor', args);
}
///Sets the background color of the refresh control.
@ -148,7 +147,7 @@ class PullToRefreshController {
Future<void> setBackgroundColor(Color color) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('color', () => color.toHex());
await _channel?.invokeMethod('setBackgroundColor', args);
await channel?.invokeMethod('setBackgroundColor', args);
}
///Set the distance to trigger a sync in dips.
@ -158,7 +157,7 @@ class PullToRefreshController {
Future<void> setDistanceToTriggerSync(int distanceToTriggerSync) async {
Map<String, dynamic> args = <String, dynamic>{};
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.
@ -168,7 +167,7 @@ class PullToRefreshController {
Future<void> setSlingshotDistance(int slingshotDistance) async {
Map<String, dynamic> args = <String, dynamic>{};
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.
@ -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()))
Future<int> getDefaultSlingshotDistance() async {
Map<String, dynamic> args = <String, dynamic>{};
return await _channel?.invokeMethod('getDefaultSlingshotDistance', args);
return await channel?.invokeMethod<int>('getDefaultSlingshotDistance', args) ?? 0;
}
///Use [setIndicatorSize] instead.
@ -185,7 +184,7 @@ class PullToRefreshController {
Future<void> setSize(AndroidPullToRefreshSize size) async {
Map<String, dynamic> args = <String, dynamic>{};
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].
@ -195,7 +194,7 @@ class PullToRefreshController {
Future<void> setIndicatorSize(PullToRefreshSize size) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('size', () => size.toNativeValue());
await _channel?.invokeMethod('setSize', args);
await channel?.invokeMethod('setSize', args);
}
///Use [setStyledTitle] instead.
@ -203,7 +202,7 @@ class PullToRefreshController {
Future<void> setAttributedTitle(IOSNSAttributedString attributedTitle) async {
Map<String, dynamic> args = <String, dynamic>{};
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.
@ -213,30 +212,21 @@ class PullToRefreshController {
Future<void> setStyledTitle(AttributedString attributedTitle) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('attributedTitle', () => attributedTitle.toMap());
await _channel?.invokeMethod('setStyledTitle', args);
await channel?.invokeMethod('setStyledTitle', args);
}
///Disposes the controller.
@override
void dispose({bool isKeepAlive = false}) {
if (!isKeepAlive) {
_channel?.setMethodCallHandler(null);
}
_channel = null;
disposeChannel(removeMethodCallHandler: !isKeepAlive);
}
}
extension InternalPullToRefreshController on PullToRefreshController {
void init(dynamic id) {
this._channel = MethodChannel(
channel = MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_$id');
this._channel?.setMethodCallHandler((call) async {
if (_channel == null) return null;
try {
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
handler = _handleMethod;
initMethodCallHandler();
}
}

View File

@ -4,8 +4,10 @@ import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'debug_logging_settings.dart';
import 'types/disposable.dart';
class Util {
static bool get isWeb => kIsWeb;
@ -582,3 +584,60 @@ void debugLog(
class Color_ extends Color {
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 'in_app_web_view_web_element.dart';
import '../util.dart';
import '../types/disposable.dart';
class HeadlessInAppWebViewWebElement implements Disposable {
class HeadlessInAppWebViewWebElement extends ChannelController {
String id;
late BinaryMessenger _messenger;
InAppWebViewWebElement? webView;
late MethodChannel? _channel;
HeadlessInAppWebViewWebElement(
{required this.id,
@ -19,23 +17,16 @@ class HeadlessInAppWebViewWebElement implements Disposable {
required this.webView}) {
this._messenger = messenger;
_channel = MethodChannel(
channel = MethodChannel(
'com.pichillilorenzo/flutter_headless_inappwebview_${this.id}',
const StandardMethodCodec(),
_messenger,
);
this._channel?.setMethodCallHandler((call) async {
try {
return await handleMethodCall(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
handler = _handleMethod;
initMethodCallHandler();
}
Future<dynamic> handleMethodCall(MethodCall call) async {
Future<dynamic> _handleMethod(MethodCall call) async {
switch (call.method) {
case "dispose":
dispose();
@ -57,7 +48,7 @@ class HeadlessInAppWebViewWebElement implements Disposable {
}
void onWebViewCreated() async {
await _channel?.invokeMethod("onWebViewCreated");
await channel?.invokeMethod("onWebViewCreated");
}
void setSize(Size size) {
@ -73,8 +64,7 @@ class HeadlessInAppWebViewWebElement implements Disposable {
@override
void dispose() {
_channel?.setMethodCallHandler(null);
_channel = null;
disposeChannel();
HeadlessInAppWebViewManager.webViews.putIfAbsent(id, () => null);
webView?.dispose();
webView = null;

View File

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

View File

@ -1,11 +1,9 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import '../util.dart';
import '../debug_logging_settings.dart';
import '../types/main.dart';
import '../types/disposable.dart';
import '../web_uri.dart';
import 'web_authenticate_session_settings.dart';
@ -37,7 +35,7 @@ typedef WebAuthenticationSessionCompletionHandler = Future<void> Function(
///**Supported Platforms/Implementations**:
///- iOS
///- MacOS
class WebAuthenticationSession implements Disposable {
class WebAuthenticationSession extends ChannelController {
///Debug settings.
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.
WebAuthenticationSessionCompletionHandler onComplete;
MethodChannel? _channel;
static const MethodChannel _sharedChannel = const MethodChannel(
'com.pichillilorenzo/flutter_webauthenticationsession');
@ -102,16 +99,10 @@ class WebAuthenticationSession implements Disposable {
id = IdGenerator.generate();
this.initialSettings =
initialSettings ?? WebAuthenticationSessionSettings();
this._channel = MethodChannel(
channel = MethodChannel(
'com.pichillilorenzo/flutter_webauthenticationsession_$id');
this._channel?.setMethodCallHandler((call) async {
try {
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
handler = _handleMethod;
initMethodCallHandler();
}
_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))
Future<bool> canStart() async {
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.
@ -161,7 +152,7 @@ class WebAuthenticationSession implements Disposable {
///- iOS ([Official API - ASWebAuthenticationSession.start](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession/2990953-start))
Future<bool> start() async {
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.
@ -173,7 +164,7 @@ class WebAuthenticationSession implements Disposable {
///- iOS ([Official API - ASWebAuthenticationSession.cancel](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession/2990951-cancel))
Future<void> cancel() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod("cancel", args);
await channel?.invokeMethod("cancel", args);
}
///Disposes the web authentication session.
@ -183,9 +174,8 @@ class WebAuthenticationSession implements Disposable {
@override
Future<void> dispose() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel?.invokeMethod("dispose", args);
_channel?.setMethodCallHandler(null);
_channel = null;
await channel?.invokeMethod("dispose", args);
disposeChannel();
}
///Returns `true` if [ASWebAuthenticationSession](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession)
@ -196,6 +186,6 @@ class WebAuthenticationSession implements Disposable {
///- iOS
static Future<bool> isAvailable() async {
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_inappwebview/src/util.dart';
import 'web_message_port.dart';
///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
///- iOS
///- MacOS
class WebMessageChannel {
class WebMessageChannel extends ChannelController {
///Message Channel ID used internally.
final String id;
@ -17,20 +18,12 @@ class WebMessageChannel {
///The second [WebMessagePort] object of the channel.
final WebMessagePort port2;
MethodChannel? _channel;
WebMessageChannel(
{required this.id, required this.port1, required this.port2}) {
this._channel = MethodChannel(
channel = MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_web_message_channel_$id');
this._channel?.setMethodCallHandler((call) async {
try {
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
handler = _handleMethod;
initMethodCallHandler();
}
static WebMessageChannel? fromMap(Map<String, dynamic>? map) {
@ -62,6 +55,12 @@ class WebMessageChannel {
return null;
}
///Disposes the web message channel.
@override
void dispose() {
disposeChannel();
}
@override
String toString() {
return 'WebMessageChannel{id: $id, port1: $port1, port2: $port2}';
@ -69,5 +68,5 @@ class 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
///- iOS
///- MacOS
class WebMessageListener {
class WebMessageListener extends ChannelController {
///Message Listener ID used internally.
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)
OnPostMessageCallback? onPostMessage;
MethodChannel? _channel;
WebMessageListener(
{required this.jsObjectName,
Set<String>? allowedOriginRules,
@ -45,16 +43,10 @@ class WebMessageListener {
allowedOriginRules != null ? allowedOriginRules : Set.from(["*"]);
assert(!this.allowedOriginRules.contains(""),
"allowedOriginRules cannot contain empty strings");
this._channel = MethodChannel(
channel= MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_web_message_listener_${id}_$jsObjectName');
this._channel?.setMethodCallHandler((call) async {
try {
return await _handleMethod(call);
} on Error catch (e) {
print(e);
print(e.stackTrace);
}
});
handler = _handleMethod;
initMethodCallHandler();
}
Future<dynamic> _handleMethod(MethodCall call) async {
@ -78,6 +70,11 @@ class WebMessageListener {
return null;
}
@override
void dispose() {
disposeChannel();
}
Map<String, dynamic> toMap() {
return {
"id": id,
@ -114,7 +111,7 @@ class JavaScriptReplyProxy {
Future<void> postMessage(String message) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('message', () => message);
await _webMessageListener._channel?.invokeMethod('postMessage', args);
await _webMessageListener.channel?.invokeMethod('postMessage', args);
}
@override

View File

@ -37,7 +37,7 @@ class WebMessagePort {
Future<void> setWebMessageCallback(WebMessageCallback? onMessage) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('index', () => this._index);
await _webMessageChannel.channel
await _webMessageChannel.internalChannel
?.invokeMethod('setWebMessageCallback', args);
this._onMessage = onMessage;
}
@ -47,14 +47,14 @@ class WebMessagePort {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('index', () => this._index);
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.
Future<void> close() async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('index', () => this._index);
await _webMessageChannel.channel?.invokeMethod('close', args);
await _webMessageChannel.internalChannel?.invokeMethod('close', args);
}
@ExchangeableObjectMethod(toMapMergeWith: true)

View File

@ -1,6 +1,7 @@
import 'dart:convert';
import '../in_app_webview/in_app_webview_controller.dart';
import '../types/disposable.dart';
import '../types/main.dart';
import 'web_storage_item.dart';
@ -12,7 +13,7 @@ import 'web_storage_item.dart';
///- iOS
///- MacOS
///- Web
class WebStorage {
class WebStorage implements Disposable {
///Represents `window.localStorage`.
LocalStorage localStorage;
@ -22,6 +23,7 @@ class WebStorage {
WebStorage({required this.localStorage, required this.sessionStorage});
///Disposes the web storage.
@override
void dispose() {
localStorage.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.
///It is used by [LocalStorage] and [SessionStorage].
class Storage {
class Storage implements Disposable {
InAppWebViewController? _controller;
///The web storage type: `window.sessionStorage` or `window.localStorage`.
@ -179,6 +181,7 @@ class Storage {
}
///Disposes the storage.
@override
void dispose() {
_controller = null;
}