added HeadlessInAppWebView class, added close, addMenuItem and addMenuItems methods to ChromeSafariBrowser, added ChromeSafariBrowserMenuItem class, fixed InAppWebView.channel null exception on android, fix #305, fix #245, fix #299

This commit is contained in:
Lorenzo Pichilli 2020-05-11 02:48:41 +02:00
parent f9ed06cc02
commit 02c3de9280
77 changed files with 3082 additions and 1980 deletions

View File

@ -1,5 +1,11 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="OTHER_INDENT_OPTIONS">
<value>
<option name="INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</value>
</option>
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />

View File

@ -1,5 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownEnhProjectSettings">
<AnnotatorSettings targetHasSpaces="true" linkCaseMismatch="true" wikiCaseMismatch="true" wikiLinkHasDashes="true" notUnderWikiHome="true" targetNotWikiPageExt="true" notUnderSourceWikiHome="true" targetNameHasAnchor="true" targetPathHasAnchor="true" wikiLinkHasSlash="true" wikiLinkHasSubdir="true" wikiLinkHasOnlyAnchor="true" linkTargetsWikiHasExt="true" linkTargetsWikiHasBadExt="true" notUnderSameRepo="true" targetNotUnderVcs="false" linkNeedsExt="true" linkHasBadExt="true" linkTargetNeedsExt="true" linkTargetHasBadExt="true" wikiLinkNotInWiki="true" imageTargetNotInRaw="true" repoRelativeAcrossVcsRoots="true" multipleWikiTargetsMatch="true" unresolvedLinkReference="true" linkIsIgnored="true" anchorIsIgnored="true" anchorIsUnresolved="true" anchorLineReferenceIsUnresolved="true" anchorLineReferenceFormat="true" anchorHasDuplicates="true" abbreviationDuplicates="true" abbreviationNotUsed="true" attributeIdDuplicateDefinition="true" attributeIdNotUsed="true" footnoteDuplicateDefinition="true" footnoteUnresolved="true" footnoteDuplicates="true" footnoteNotUsed="true" macroDuplicateDefinition="true" macroUnresolved="true" macroDuplicates="true" macroNotUsed="true" referenceDuplicateDefinition="true" referenceUnresolved="true" referenceDuplicates="true" referenceNotUsed="true" referenceUnresolvedNumericId="true" enumRefDuplicateDefinition="true" enumRefUnresolved="true" enumRefDuplicates="true" enumRefNotUsed="true" enumRefLinkUnresolved="true" enumRefLinkDuplicates="true" simTocUpdateNeeded="true" simTocTitleSpaceNeeded="true" />
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="css" scriptDir="js" plainHtml="false" imageDir="" copyLinkedImages="false" imagePathType="0" targetPathType="2" targetExt="" useTargetExt="false" noCssNoScripts="false" useElementStyleAttribute="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
<component name="MarkdownNavigatorHistory">
<PasteImageHistory checkeredTransparentBackground="false" filename="image" directory="" onPasteImageTargetRef="3" onPasteLinkText="0" onPasteImageElement="1" onPasteLinkElement="1" onPasteReferenceElement="2" cornerRadius="20" borderColor="0" transparentColor="16777215" borderWidth="1" trimTop="0" trimBottom="0" trimLeft="0" trimRight="0" transparent="false" roundCorners="false" showPreview="true" bordered="false" scaled="false" cropped="false" hideInapplicableOperations="false" preserveLinkFormat="false" scale="50" scalingInterpolation="1" transparentTolerance="0" saveAsDefaultOnOK="false" linkFormat="0" addHighlights="false" showHighlightCoordinates="true" showHighlights="false" mouseSelectionAddsHighlight="false" outerFilled="false" outerFillColor="0" outerFillTransparent="true" outerFillAlpha="30">
<highlightList />
<directories />
<filenames />
</PasteImageHistory>
<CopyImageHistory checkeredTransparentBackground="false" filename="image" directory="" onPasteImageTargetRef="3" onPasteLinkText="0" onPasteImageElement="1" onPasteLinkElement="1" onPasteReferenceElement="2" cornerRadius="20" borderColor="0" transparentColor="16777215" borderWidth="1" trimTop="0" trimBottom="0" trimLeft="0" trimRight="0" transparent="false" roundCorners="false" showPreview="true" bordered="false" scaled="false" cropped="false" hideInapplicableOperations="false" preserveLinkFormat="false" scale="50" scalingInterpolation="1" transparentTolerance="0" saveAsDefaultOnOK="false" linkFormat="0" addHighlights="false" showHighlightCoordinates="true" showHighlights="false" mouseSelectionAddsHighlight="false" outerFilled="false" outerFillColor="0" outerFillTransparent="true" outerFillAlpha="30">
<highlightList />
<directories />
<filenames />
</CopyImageHistory>
<PasteLinkHistory onPasteImageTargetRef="3" onPasteTargetRef="1" onPasteLinkText="0" onPasteImageElement="1" onPasteLinkElement="1" onPasteWikiElement="2" onPasteReferenceElement="2" hideInapplicableOperations="false" preserveLinkFormat="false" useHeadingForLinkText="false" linkFormat="0" saveAsDefaultOnOK="false" />
<TableToJsonHistory>
<entries />
</TableToJsonHistory>
<TableSortHistory>
<entries />
</TableSortHistory>
</component>
</project>

View File

@ -1,65 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownProjectSettings" wasCopied="false">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" openRemoteLinks="true" replaceUnicodeEmoji="false" lastLayoutSetsDefault="false">
<component name="FlexmarkProjectSettings">
<FlexmarkHtmlSettings flexmarkSpecExampleRendering="0" flexmarkSpecExampleRenderHtml="false">
<flexmarkSectionLanguages>
<option name="1" value="Markdown" />
<option name="2" value="HTML" />
<option name="3" value="flexmark-ast:1" />
</flexmarkSectionLanguages>
</FlexmarkHtmlSettings>
</component>
<component name="MarkdownProjectSettings">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" lastLayoutSetsDefault="false">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
<provider providerId="com.vladsch.md.nav.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false" emojiShortcuts="1" emojiImages="0">
<ParserSettings gitHubSyntaxChange="false" correctedInvalidSettings="false" emojiShortcuts="1" emojiImages="0">
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="HTML_DEEP_PARSER" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="INTELLIJ_DUMMY_IDENTIFIER" value="true" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="ADMONITION_EXT" value="false" />
<option name="ATTRIBUTES_EXT" value="false" />
<option name="COMMONMARK_LISTS" value="true" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="ENUMERATED_REFERENCES_EXT" value="false" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_LISTS" value="false" />
<option name="GITHUB_WIKI_LINKS" value="true" />
<option name="GITLAB_EXT" value="false" />
<option name="GITLAB_MATH_EXT" value="false" />
<option name="GITLAB_MERMAID_EXT" value="false" />
<option name="HEADER_ID_NO_DUPED_DASHES" value="false" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="MACROS_EXT" value="false" />
<option name="NO_TEXT_ATTRIBUTES" value="false" />
<option name="PARSE_HTML_ANCHOR_ID" value="false" />
<option name="PRODUCTION_SPEC_PARSER" value="true" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false" imageUriSerials="false" addDocTypeHtml="true">
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" addPageHeader="true" imageUriSerials="false" addDocTypeHtml="true" noParaTags="false" plantUmlConversion="0">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
<provider providerId="com.vladsch.md.nav.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
@ -68,15 +47,11 @@
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
<provider providerId="com.vladsch.md.nav.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
<cssUriHistory />
</CssSettings>
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
</project>

View File

@ -1,3 +0,0 @@
<component name="MarkdownNavigator.ProfileManager">
<settings default="" pdf-export="" />
</component>

View File

@ -1,3 +1,15 @@
## 3.1.0
- Added `HeadlessInAppWebView` class to be able to use WebView in headless mode
- Added `close`, `addMenuItem`, `addMenuItems` methods to `ChromeSafariBrowser`
- Added `ChromeSafariBrowserMenuItem` class in order to create custom menu item for `ChromeSafariBrowser`.
- Fixed `InAppWebView.channel` null when used by `InAppBrowserActivity` on android
- Fixed iOS presentationStyle affecting only dismiss animation [#305](https://github.com/pichillilorenzo/flutter_inappwebview/issues/305)
### BREAKING CHANGES
- Renamed `InAppWebViewWidgetOptions` to `InAppWebViewGroupOptions`.
## 3.0.0
- Added `Promise` javascript [polyfill](https://github.com/tildeio/rsvp.js) for webviews that doesn't support it for `window.flutter_inappwebview.callHandler`

149
README.md
View File

@ -68,6 +68,7 @@ First, add `flutter_inappwebview` as a [dependency in your pubspec.yaml file](ht
Classes:
- [InAppWebView](#inappwebview-class): Flutter Widget for adding an **inline native WebView** integrated into the flutter widget tree. To use `InAppWebView` class on iOS you need to opt-in for the embedded views preview by adding a boolean property to the app's `Info.plist` file, with the key `io.flutter.embedded_views_preview` and the value `YES`.
- [HeadlessInAppWebView](#headlessinappwebview-class): Class that represents a WebView in headless mode. It can be used to run a WebView in background without attaching an `InAppWebView` to the widget tree.
- [InAppBrowser](#inappbrowser-class): In-App Browser using native WebView.
- [ChromeSafariBrowser](#chromesafaribrowser-class): In-App Browser using [Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android / [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS.
- [InAppLocalhostServer](#inapplocalhostserver-class): This class allows you to create a simple server on `http://localhost:[port]/`. The default `port` value is `8080`.
@ -132,7 +133,7 @@ Future main() async {
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
}z
class _MyAppState extends State<MyApp> {
@ -177,7 +178,7 @@ class _MyAppState extends State<MyApp> {
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
)
@ -280,7 +281,7 @@ Screenshots:
* `addJavaScriptHandler({@required String handlerName, @required JavaScriptHandlerCallback callback})`: Adds a JavaScript message handler callback that listen to post messages sent from JavaScript by the handler with name `handlerName`.
* `removeJavaScriptHandler({@required String handlerName})`: Removes a JavaScript message handler previously added with the `addJavaScriptHandler()` associated to `handlerName` key.
* `takeScreenshot`: Takes a screenshot (in PNG format) of the WebView's visible viewport and returns a `Uint8List`. Returns `null` if it wasn't be able to take it.
* `setOptions({@required InAppWebViewWidgetOptions options})`: Sets the WebView options with the new options and evaluates them.
* `setOptions({@required InAppWebViewGroupOptions options})`: Sets the WebView options with the new options and evaluates them.
* `getOptions`: Gets the current WebView options. Returns the options with `null` value if they are not set yet.
* `getCopyBackForwardList`: Gets the `WebHistory` for this WebView. This contains the back/forward list for use in querying each item in the history stack.
* `clearCache`: Clears all the webview's cache.
@ -333,8 +334,8 @@ This event will be dispatched as soon as the platform (Android or iOS) is ready
```
`window.flutter_inappwebview.callHandler` returns a JavaScript [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
that can be used to get the json result returned by [JavaScriptHandlerCallback].
In this case, simply return data that you want to send and it will be automatically json encoded using [jsonEncode] from the `dart:convert` library.
that can be used to get the json result returned by `JavaScriptHandlerCallback.
In this case, simply return data that you want to send and it will be automatically json encoded using `jsonEncode` from the `dart:convert` library.
So, on the JavaScript side, to get data coming from the Dart side, you will use:
```html
@ -498,6 +499,127 @@ Event names that starts with `android` or `ios` are events platform-specific.
* `iosOnDidCommit`: Called when the web view begins to receive web content (available only on iOS).
* `iosOnDidReceiveServerRedirectForProvisionalNavigation`: Called when a web view receives a server redirect (available only on iOS).
### `HeadlessInAppWebView` class
Class that represents a WebView in headless mode. It can be used to run a WebView in background without attaching an `InAppWebView` to the widget tree.
Remember to dispose it when you don't need it anymore.
As `InAppWebView`, it has the same options and events. Use `InAppWebViewController` to control the WebView instance.
Example:
```dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
HeadlessInAppWebView headlessWebView;
String url = "";
@override
void initState() {
super.initState();
headlessWebView = new HeadlessInAppWebView(
initialUrl: "https://flutter.dev/",
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
),
),
onWebViewCreated: (controller) {
print('HeadlessInAppWebView created!');
},
onConsoleMessage: (controller, consoleMessage) {
print("CONSOLE MESSAGE: " + consoleMessage.message);
},
onLoadStart: (controller, url) async {
print("onLoadStart $url");
setState(() {
this.url = url;
});
},
onLoadStop: (controller, url) async {
print("onLoadStop $url");
setState(() {
this.url = url;
});
},
onUpdateVisitedHistory: (InAppWebViewController controller, String url, bool androidIsReload) {
print("onUpdateVisitedHistory $url");
setState(() {
this.url = url;
});
},
);
}
@override
void dispose() {
super.dispose();
headlessWebView.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"HeadlessInAppWebView",
)),
drawer: myDrawer(context: context),
body: SafeArea(
child: Column(children: <Widget>[
Container(
padding: EdgeInsets.all(20.0),
child: Text(
"CURRENT URL\n${(url.length > 50) ? url.substring(0, 50) + "..." : url}"),
),
Center(
child: RaisedButton(
onPressed: () async {
await headlessWebView.dispose();
await headlessWebView.run();
},
child: Text("Run HeadlessInAppWebView")),
),
Center(
child: RaisedButton(
onPressed: () async {
try {
await headlessWebView.webViewController.evaluateJavascript(source: """console.log('Here is the message!');""");
} on MissingPluginException catch(e) {
print("HeadlessInAppWebView is not running. Click on \"Run HeadlessInAppWebView\"!");
}
},
child: Text("Send console.log message")),
),
Center(
child: RaisedButton(
onPressed: () {
headlessWebView.dispose();
},
child: Text("Dispose HeadlessInAppWebView")),
)
])
)
);
}
}
```
### `InAppBrowser` class
In-App Browser using native WebView.
@ -596,7 +718,7 @@ class _MyAppState extends State<MyApp> {
widget.browser.openFile(
assetFilePath: "assets/index.html",
options: InAppBrowserClassOptions(
inAppWebViewWidgetOptions: InAppWebViewWidgetOptions(
inAppWebViewGroupOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: true,
useOnLoadResource: true,
@ -744,6 +866,16 @@ class _MyAppState extends State<MyApp> {
@override
void initState() {
widget.browser.addMenuItem(new ChromeSafariBrowserMenuItem(id: 1, label: 'Custom item menu 1', action: (url, title) {
print('Custom item menu 1 clicked!');
print(url);
print(title);
}));
widget.browser.addMenuItem(new ChromeSafariBrowserMenuItem(id: 2, label: 'Custom item menu 2', action: (url, title) {
print('Custom item menu 2 clicked!');
print(url);
print(title);
}));
super.initState();
}
@ -785,6 +917,9 @@ Screenshots:
* `open({@required String url, ChromeSafariBrowserClassOptions options, Map<String, String> headersFallback = const {}, InAppBrowserClassOptions optionsFallback})`: Opens an `url` in a new `ChromeSafariBrowser` instance.
* `isOpened`: Returns `true` if the `ChromeSafariBrowser` instance is opened, otherwise `false`.
* `close`: Closes the `ChromeSafariBrowser` instance.
* `addMenuItem`: Adds a `ChromeSafariBrowserMenuItem` to the menu.
* `addMenuItems`: Adds a list of `ChromeSafariBrowserMenuItem` to the menu.
#### `ChromeSafariBrowser` options
@ -846,7 +981,7 @@ Future main() async {
child: InAppWebView(
initialUrl: "http://localhost:8080/assets/index.html",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
inAppWebViewOptions: InAppWebViewOptions(
debuggingEnabled: true,
)

View File

@ -2,8 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.pichillilorenzo.flutter_inappwebview">
<application>
<activity android:theme="@style/AppTheme" android:name="com.pichillilorenzo.flutter_inappwebview.InAppBrowserActivity" android:configChanges="orientation|screenSize"></activity>
<activity android:theme="@style/AppTheme" android:name="com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity" android:configChanges="orientation|screenSize"></activity>
<activity android:theme="@style/ThemeTransparent" android:name="com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.ChromeCustomTabsActivity" android:configChanges="orientation|screenSize"></activity>
<receiver android:name="com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.ActionBroadcastReceiver" />
</application>
</manifest>

View File

@ -0,0 +1,39 @@
package com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import java.util.HashMap;
import java.util.Map;
import io.flutter.plugin.common.MethodChannel;
public class ActionBroadcastReceiver extends BroadcastReceiver {
protected static final String LOG_TAG = "ActionBroadcastReceiver";
public static final String KEY_ACTION_ID = "com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.ACTION_ID";
public static final String KEY_ACTION_UUID = "com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.ACTION_UUID";
public static final String KEY_URL_TITLE = "android.intent.extra.SUBJECT";
@Override
public void onReceive(Context context, Intent intent) {
String url = intent.getDataString();
if (url != null) {
Bundle b = intent.getExtras();
String uuid = b.getString(KEY_ACTION_UUID);
int id = b.getInt(KEY_ACTION_ID);
String title = b.getString(KEY_URL_TITLE);
MethodChannel channel = new MethodChannel(Shared.messenger, "com.pichillilorenzo/flutter_chromesafaribrowser_" + uuid);
Map<String, Object> obj = new HashMap<>();
obj.put("url", url);
obj.put("title", title);
obj.put("id", id);
channel.invokeMethod("onChromeSafariBrowserMenuItemActionPerform", obj);
}
}
}

View File

@ -1,21 +1,24 @@
package com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import androidx.browser.customtabs.CustomTabsCallback;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.browser.customtabs.CustomTabsService;
import androidx.browser.customtabs.CustomTabsSession;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
import com.pichillilorenzo.flutter_inappwebview.R;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.flutter.plugin.common.MethodCall;
@ -52,6 +55,8 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel.
options = new ChromeCustomTabsOptions();
options.parse((HashMap<String, Object>) b.getSerializable("options"));
final List<HashMap<String, Object>> menuItemList = (List<HashMap<String, Object>>) b.getSerializable("menuItemList");
final ChromeCustomTabsActivity chromeCustomTabsActivity = this;
customTabActivityHelper = new CustomTabActivityHelper();
@ -63,18 +68,17 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel.
customTabActivityHelper.mayLaunchUrl(uri, null, null);
builder = new CustomTabsIntent.Builder(customTabsSession);
prepareCustomTabs(menuItemList);
CustomTabsIntent customTabsIntent = builder.build();
prepareCustomTabs(customTabsIntent);
prepareCustomTabsIntent(customTabsIntent);
CustomTabActivityHelper.openCustomTab(chromeCustomTabsActivity, customTabsIntent, uri, CHROME_CUSTOM_TAB_REQUEST_CODE);
}
@Override
public void onCustomTabsDisconnected() {
customTabsSession = null;
finish();
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", uuid);
channel.invokeMethod("onChromeSafariBrowserClosed", obj);
chromeCustomTabsActivity.close();
}
});
@ -122,12 +126,23 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel.
@Override
public void onMethodCall(final MethodCall call, final MethodChannel.Result result) {
switch (call.method) {
case "close":
this.onStop();
this.onDestroy();
this.close();
// https://stackoverflow.com/a/41596629/4637638
Intent myIntent = new Intent(Shared.activity, Shared.activity.getClass());
myIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
myIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
Shared.activity.startActivity(myIntent);
break;
default:
result.notImplemented();
}
}
private void prepareCustomTabs(CustomTabsIntent customTabsIntent) {
private void prepareCustomTabs(List<HashMap<String, Object>> menuItemList) {
if (options.addDefaultShareMenuItem)
builder.addDefaultShareMenuItem();
@ -141,6 +156,14 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel.
builder.setInstantAppsEnabled(options.instantAppsEnabled);
for (HashMap<String, Object> menuItem : menuItemList) {
int id = (int) menuItem.get("id");
String label = (String) menuItem.get("label");
builder.addMenuItem(label, createPendingIntent(id));
}
}
private void prepareCustomTabsIntent(CustomTabsIntent customTabsIntent) {
if (options.packageName != null)
customTabsIntent.intent.setPackage(options.packageName);
else
@ -165,12 +188,27 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CHROME_CUSTOM_TAB_REQUEST_CODE) {
customTabsSession = null;
finish();
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", uuid);
InAppWebViewFlutterPlugin.inAppBrowserManager.channel.invokeMethod("onChromeSafariBrowserClosed", obj);
close();
}
}
public void close() {
customTabsSession = null;
finish();
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", uuid);
channel.invokeMethod("onChromeSafariBrowserClosed", obj);
}
private PendingIntent createPendingIntent(int actionSourceId) {
Intent actionIntent = new Intent(this, ActionBroadcastReceiver.class);
Bundle extras = new Bundle();
extras.putInt(ActionBroadcastReceiver.KEY_ACTION_ID, actionSourceId);
extras.putString(ActionBroadcastReceiver.KEY_ACTION_UUID, uuid);
actionIntent.putExtras(extras);
return PendingIntent.getBroadcast(
this, actionSourceId, actionIntent, 0);
}
}

View File

@ -7,9 +7,12 @@ import android.util.Log;
import com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.ChromeCustomTabsActivity;
import com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.CustomTabActivityHelper;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserOptions;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.flutter.plugin.common.BinaryMessenger;
@ -40,7 +43,8 @@ public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandl
String uuidFallback = (String) call.argument("uuidFallback");
Map<String, String> headersFallback = (Map<String, String>) call.argument("headersFallback");
HashMap<String, Object> optionsFallback = (HashMap<String, Object>) call.argument("optionsFallback");
open(activity, uuid, url, options, uuidFallback, headersFallback, optionsFallback, result);
List<HashMap<String, Object>> menuItemList = (List<HashMap<String, Object>>) call.argument("menuItemList");
open(activity, uuid, url, options, uuidFallback, headersFallback, optionsFallback, menuItemList, result);
}
break;
default:
@ -48,7 +52,8 @@ public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandl
}
}
public void open(Activity activity, String uuid, String url, HashMap<String, Object> options, String uuidFallback, Map<String, String> headersFallback, HashMap<String, Object> optionsFallback, MethodChannel.Result result) {
public void open(Activity activity, String uuid, String url, HashMap<String, Object> options, String uuidFallback,
Map<String, String> headersFallback, HashMap<String, Object> optionsFallback, List<HashMap<String, Object>> menuItemList, MethodChannel.Result result) {
Intent intent = null;
Bundle extras = new Bundle();
@ -58,6 +63,7 @@ public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandl
extras.putString("uuid", uuid);
extras.putSerializable("options", options);
extras.putSerializable("headers", (Serializable) headersFallback);
extras.putSerializable("menuItemList", (Serializable) menuItemList);
if (CustomTabActivityHelper.isAvailable(activity)) {
intent = new Intent(activity, ChromeCustomTabsActivity.class);

View File

@ -0,0 +1,88 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package com.pichillilorenzo.flutter_inappwebview;
import android.app.Activity;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.FlutterWebView;
import java.util.HashMap;
import java.util.Map;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.Result;
/**
* InAppBrowserManager
*/
public class HeadlessInAppWebViewManager implements MethodChannel.MethodCallHandler {
public MethodChannel channel;
protected static final String LOG_TAG = "HeadlessInAppWebViewManager";
Map<String, FlutterWebView> flutterWebViews = new HashMap<>();
public HeadlessInAppWebViewManager(BinaryMessenger messenger) {
channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_headless_inappwebview");
channel.setMethodCallHandler(this);
}
@Override
public void onMethodCall(final MethodCall call, final Result result) {
final Activity activity = Shared.activity;
final String uuid = (String) call.argument("uuid");
switch (call.method) {
case "createHeadlessWebView":
{
HashMap<String, Object> params = (HashMap<String, Object>) call.argument("params");
createHeadlessWebView(activity, uuid, params);
}
result.success(true);
break;
case "disposeHeadlessWebView":
disposeHeadlessWebView(uuid);
result.success(true);
break;
default:
result.notImplemented();
}
}
public void createHeadlessWebView(Activity activity, String uuid, HashMap<String, Object> params) {
FlutterWebView flutterWebView = new FlutterWebView(Shared.messenger, activity, uuid, params, null);
flutterWebViews.put(uuid, flutterWebView);
}
public void disposeHeadlessWebView(String uuid) {
if (flutterWebViews.containsKey(uuid)) {
flutterWebViews.get(uuid).dispose();
flutterWebViews.remove(uuid);
}
}
public void dispose() {
channel.setMethodCallHandler(null);
}
}

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview;
package com.pichillilorenzo.flutter_inappwebview.InAppBrowser;
import android.content.Intent;
import android.graphics.Bitmap;
@ -26,6 +26,8 @@ import androidx.appcompat.app.AppCompatActivity;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewOptions;
import com.pichillilorenzo.flutter_inappwebview.R;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@ -65,6 +67,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
webView = findViewById(R.id.webView);
webView.inAppBrowserActivity = this;
webView.channel = channel;
fromActivity = b.getString("fromActivity");

View File

@ -1,4 +1,6 @@
package com.pichillilorenzo.flutter_inappwebview;
package com.pichillilorenzo.flutter_inappwebview.InAppBrowser;
import com.pichillilorenzo.flutter_inappwebview.Options;
import java.util.HashMap;
import java.util.Map;

View File

@ -32,6 +32,8 @@ import android.os.Bundle;
import android.webkit.MimeTypeMap;
import android.util.Log;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;

View File

@ -1,4 +1,4 @@
package com.pichillilorenzo.flutter_inappwebview;
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
import android.app.Activity;
import android.content.Context;
@ -14,6 +14,8 @@ import android.webkit.WebViewClient;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.DisplayListenerProxy;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewOptions;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.Util;
import java.io.IOException;
import java.lang.reflect.Field;
@ -37,7 +39,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
public InAppWebView webView;
public final MethodChannel channel;
public FlutterWebView(BinaryMessenger messenger, final Context context, int id, HashMap<String, Object> params, View containerView) {
public FlutterWebView(BinaryMessenger messenger, final Context context, Object id, HashMap<String, Object> params, View containerView) {
channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_" + id);
channel.setMethodCallHandler(this);
@ -93,6 +95,12 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
}
else
webView.loadUrl(initialUrl, initialHeaders);
if (containerView == null && id instanceof String) {
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", id);
channel.invokeMethod("onHeadlessWebViewCreated", obj);
}
}
@Override

View File

@ -1,12 +1,13 @@
package com.pichillilorenzo.flutter_inappwebview;
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
import android.content.Context;
import android.view.View;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.FlutterWebView;
import java.util.HashMap;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;

View File

@ -5,19 +5,16 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.print.PrintManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
import android.webkit.CookieManager;
import android.webkit.DownloadListener;
import android.webkit.ValueCallback;
@ -27,17 +24,12 @@ import android.webkit.WebSettings;
import android.webkit.WebStorage;
import androidx.annotation.RequiresApi;
import androidx.appcompat.widget.PopupMenu;
import android.view.ActionMode;
import android.webkit.WebView;
import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlocker;
import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlockerAction;
import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlockerHandler;
import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlockerTrigger;
import com.pichillilorenzo.flutter_inappwebview.FlutterWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.JavaScriptBridgeInterface;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.Util;
@ -62,7 +54,7 @@ final public class InAppWebView extends InputAwareWebView {
public InAppBrowserActivity inAppBrowserActivity;
public FlutterWebView flutterWebView;
public MethodChannel channel;
public int id;
public Object id;
public InAppWebViewClient inAppWebViewClient;
public InAppWebViewChromeClient inAppWebViewChromeClient;
public InAppWebViewOptions options;
@ -527,7 +519,7 @@ final public class InAppWebView extends InputAwareWebView {
super(context, attrs, defaultStyle);
}
public InAppWebView(Context context, Object obj, int id, InAppWebViewOptions options, View containerView) {
public InAppWebView(Context context, Object obj, Object id, InAppWebViewOptions options, View containerView) {
super(context, containerView);
if (obj instanceof InAppBrowserActivity)
this.inAppBrowserActivity = (InAppBrowserActivity) obj;
@ -891,7 +883,8 @@ final public class InAppWebView extends InputAwareWebView {
}
public void takeScreenshot(final MethodChannel.Result result) {
post(new Runnable() {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
int height = (int) (getContentHeight() * scale + 0.5);
@ -1199,7 +1192,8 @@ final public class InAppWebView extends InputAwareWebView {
scriptToInject = String.format(jsWrapper, jsonSourceString);
}
final String finalScriptToInject = scriptToInject;
post(new Runnable() {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {

View File

@ -27,8 +27,7 @@ import android.widget.LinearLayout;
import androidx.appcompat.app.AlertDialog;
import com.pichillilorenzo.flutter_inappwebview.FlutterWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
import com.pichillilorenzo.flutter_inappwebview.R;
import com.pichillilorenzo.flutter_inappwebview.Shared;

View File

@ -24,9 +24,7 @@ import androidx.annotation.RequiresApi;
import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.Credential;
import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.CredentialDatabase;
import com.pichillilorenzo.flutter_inappwebview.FlutterWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.JavaScriptBridgeInterface;
import com.pichillilorenzo.flutter_inappwebview.Util;

View File

@ -6,6 +6,8 @@ import android.net.Uri;
import android.os.Build;
import android.webkit.ValueCallback;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.FlutterWebViewFactory;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.BinaryMessenger;
@ -19,6 +21,7 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
protected static final String LOG_TAG = "InAppWebViewFlutterPL";
public static InAppBrowserManager inAppBrowserManager;
public static HeadlessInAppWebViewManager headlessInAppWebViewManager;
public static ChromeSafariBrowserManager chromeSafariBrowserManager;
public static InAppWebViewStatic inAppWebViewStatic;
public static MyCookieManager myCookieManager;
@ -49,6 +52,7 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
Shared.messenger = messenger;
inAppBrowserManager = new InAppBrowserManager(messenger);
headlessInAppWebViewManager = new HeadlessInAppWebViewManager(messenger);
chromeSafariBrowserManager = new ChromeSafariBrowserManager(messenger);
platformViewRegistry.registerViewFactory(
@ -67,6 +71,10 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
inAppBrowserManager.dispose();
inAppBrowserManager = null;
}
if (headlessInAppWebViewManager != null) {
headlessInAppWebViewManager.dispose();
headlessInAppWebViewManager = null;
}
if (chromeSafariBrowserManager != null) {
chromeSafariBrowserManager.dispose();
chromeSafariBrowserManager = null;

View File

@ -7,6 +7,8 @@ import android.util.Log;
import android.webkit.JavascriptInterface;
import android.webkit.ValueCallback;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.FlutterWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView;
import java.util.HashMap;

View File

@ -7,7 +7,7 @@
android:layout_height="match_parent"
android:clickable="true"
android:focusableInTouchMode="true"
tools:context=".InAppBrowserActivity"
tools:context=".InAppBrowser.InAppBrowserActivity"
android:focusable="true">
<com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView

View File

@ -3,7 +3,7 @@
xmlns:appcompat="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".InAppBrowserActivity">
tools:context=".InAppBrowser.InAppBrowserActivity">
<item
android:id="@+id/action_go_back"

View File

@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/connectivity-0.4.8+5/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.7/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-3.3.0/","dependencies":[]}],"android":[{"name":"connectivity","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/connectivity-0.4.8+5/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.7/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-3.3.0/","dependencies":[]}],"macos":[{"name":"connectivity_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/connectivity_macos-0.1.0+3/","dependencies":[]},{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+2/","dependencies":[]}],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"connectivity","dependencies":["connectivity_macos"]},{"name":"connectivity_macos","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos"]},{"name":"path_provider_macos","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-05-09 04:59:17.693618","version":"1.17.0"}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/connectivity-0.4.8+5/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.7/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-3.3.0/","dependencies":[]}],"android":[{"name":"connectivity","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/connectivity-0.4.8+5/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.7/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-3.3.0/","dependencies":[]}],"macos":[{"name":"connectivity_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/connectivity_macos-0.1.0+3/","dependencies":[]},{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+2/","dependencies":[]}],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"connectivity","dependencies":["connectivity_macos"]},{"name":"connectivity_macos","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos"]},{"name":"path_provider_macos","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-05-11 02:42:13.697935","version":"1.17.0"}

View File

@ -1,12 +0,0 @@
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter"
export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example"
export "FLUTTER_TARGET=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/lib/main.dart"
export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "OTHER_LDFLAGS=$(inherited) -framework Flutter"
export "FLUTTER_FRAMEWORK_DIR=/Users/lorenzopichilli/flutter/bin/cache/artifacts/engine/ios"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
export "TRACK_WIDGET_CREATION=true"

View File

@ -35,6 +35,16 @@ class _ChromeSafariBrowserExampleScreenState
extends State<ChromeSafariBrowserExampleScreen> {
@override
void initState() {
widget.browser.addMenuItem(new ChromeSafariBrowserMenuItem(id: 1, label: 'Custom item menu 1', action: (url, title) {
print('Custom item menu 1 clicked!');
print(url);
print(title);
}));
widget.browser.addMenuItem(new ChromeSafariBrowserMenuItem(id: 2, label: 'Custom item menu 2', action: (url, title) {
print('Custom item menu 2 clicked!');
print(url);
print(title);
}));
super.initState();
}
@ -53,7 +63,10 @@ class _ChromeSafariBrowserExampleScreenState
url: "https://flutter.dev/",
options: ChromeSafariBrowserClassOptions(
android: AndroidChromeCustomTabsOptions(addDefaultShareMenuItem: false, keepAliveEnabled: true),
ios: IOSSafariOptions(barCollapsingEnabled: true)));
ios: IOSSafariOptions(
dismissButtonStyle: IOSSafariDismissButtonStyle.CLOSE,
presentationStyle: IOSUIModalPresentationStyle.OVER_FULL_SCREEN
)));
},
child: Text("Open Chrome Safari Browser")),
));

View File

@ -0,0 +1,108 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'main.dart';
class HeadlessInAppWebViewExampleScreen extends StatefulWidget {
@override
_HeadlessInAppWebViewExampleScreenState createState() =>
new _HeadlessInAppWebViewExampleScreenState();
}
class _HeadlessInAppWebViewExampleScreenState extends State<HeadlessInAppWebViewExampleScreen> {
HeadlessInAppWebView headlessWebView;
String url = "";
@override
void initState() {
super.initState();
headlessWebView = new HeadlessInAppWebView(
initialUrl: "https://flutter.dev/",
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
),
),
onWebViewCreated: (controller) {
print('HeadlessInAppWebView created!');
},
onConsoleMessage: (controller, consoleMessage) {
print("CONSOLE MESSAGE: " + consoleMessage.message);
},
onLoadStart: (controller, url) async {
print("onLoadStart $url");
setState(() {
this.url = url;
});
},
onLoadStop: (controller, url) async {
print("onLoadStop $url");
setState(() {
this.url = url;
});
},
onUpdateVisitedHistory: (InAppWebViewController controller, String url, bool androidIsReload) {
print("onUpdateVisitedHistory $url");
setState(() {
this.url = url;
});
},
);
}
@override
void dispose() {
super.dispose();
headlessWebView.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"HeadlessInAppWebView",
)),
drawer: myDrawer(context: context),
body: SafeArea(
child: Column(children: <Widget>[
Container(
padding: EdgeInsets.all(20.0),
child: Text(
"CURRENT URL\n${(url.length > 50) ? url.substring(0, 50) + "..." : url}"),
),
Center(
child: RaisedButton(
onPressed: () async {
await headlessWebView.dispose();
await headlessWebView.run();
},
child: Text("Run HeadlessInAppWebView")),
),
Center(
child: RaisedButton(
onPressed: () async {
try {
await headlessWebView.webViewController.evaluateJavascript(source: """console.log('Here is the message!');""");
} on MissingPluginException catch(e) {
print("HeadlessInAppWebView is not running. Click on \"Run HeadlessInAppWebView\"!");
}
},
child: Text("Send console.log message")),
),
Center(
child: RaisedButton(
onPressed: () {
headlessWebView.dispose();
},
child: Text("Dispose HeadlessInAppWebView")),
)
])
)
);
}
}

View File

@ -90,7 +90,7 @@ class _InAppBrowserExampleScreenState extends State<InAppBrowserExampleScreen> {
await widget.browser.openFile(
assetFilePath: "assets/index.html",
options: InAppBrowserClassOptions(
inAppWebViewWidgetOptions: InAppWebViewWidgetOptions(
inAppWebViewGroupOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
useShouldOverrideUrlLoading: true,

View File

@ -52,7 +52,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
initialUrl: "https://flutter.dev/",
// initialFile: "assets/index.html",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
),

View File

@ -2,8 +2,8 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_inappwebview_example/chrome_safari_browser_example.screen.dart';
import 'package:flutter_inappwebview_example/headless_in_app_webview.screen.dart';
import 'package:flutter_inappwebview_example/in_app_webiew_example.screen.dart';
import 'package:flutter_inappwebview_example/in_app_browser_example.screen.dart';
@ -44,6 +44,12 @@ Drawer myDrawer({@required BuildContext context}) {
Navigator.pushReplacementNamed(context, '/');
},
),
ListTile(
title: Text('HeadlessInAppWebView'),
onTap: () {
Navigator.pushReplacementNamed(context, '/HeadlessInAppWebView');
},
),
],
),
);
@ -74,6 +80,7 @@ class _MyAppState extends State<MyApp> {
'/': (context) => InAppWebViewExampleScreen(),
'/InAppBrowser': (context) => InAppBrowserExampleScreen(),
'/ChromeSafariBrowser': (context) => ChromeSafariBrowserExampleScreen(),
'/HeadlessInAppWebView': (context) => HeadlessInAppWebViewExampleScreen(),
}
);
}

View File

@ -56,7 +56,7 @@ class InAppWebViewAjaxTestState extends WidgetTestState {
</html>
"""),
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,

View File

@ -29,7 +29,7 @@ class InAppWebViewContentBlockerTestState extends WidgetTestState {
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,

View File

@ -30,7 +30,7 @@ class InAppWebViewCookieManagerTestState extends WidgetTestState {
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -74,7 +74,7 @@ class InAppWebViewFetchTestState extends WidgetTestState {
</html>
"""),
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,

View File

@ -37,7 +37,7 @@ class InAppWebViewHttpAuthCredentialDatabaseTestState extends WidgetTestState {
child: InAppWebView(
initialUrl: "http://${environment["NODE_SERVER_IP"]}:8081/",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -65,7 +65,7 @@ class InAppWebViewInitialDataTestState extends WidgetTestState {
</html>
"""),
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -28,7 +28,7 @@ class InAppWebViewInitialFileTestState extends WidgetTestState {
child: InAppWebView(
initialFile: "test_assets/in_app_webview_initial_file_test.html",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -29,7 +29,7 @@ class InAppWebViewInitialUrlTestState extends WidgetTestState {
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -43,7 +43,7 @@ class InAppWebViewJavaScriptHandlerTestState extends WidgetTestState {
child: InAppWebView(
initialFile: "test_assets/in_app_webview_javascript_handler_test.html",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -28,7 +28,7 @@ class InAppWebViewOnConsoleMessageTestState extends WidgetTestState {
child: InAppWebView(
initialFile: "test_assets/in_app_webview_on_console_message_test.html",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -28,7 +28,7 @@ class InAppWebViewOnCreateWindowTestState extends WidgetTestState {
child: InAppWebView(
initialFile: "test_assets/in_app_webview_on_create_window_test.html",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,

View File

@ -48,7 +48,7 @@ class InAppWebViewOnDownloadStartTestState extends WidgetTestState {
</html>
"""),
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,

View File

@ -28,7 +28,7 @@ class InAppWebViewOnFindResultReceivedTestState extends WidgetTestState {
child: InAppWebView(
initialFile: "test_assets/in_app_webview_initial_file_test.html",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -36,7 +36,7 @@ class InAppWebViewOnJsDialogTestState extends WidgetTestState {
child: InAppWebView(
initialFile: "test_assets/in_app_webview_on_js_dialog_test.html",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -29,7 +29,7 @@ class InAppWebViewOnLoadErrorTestState extends WidgetTestState {
child: InAppWebView(
initialUrl: "https://not-existing-domain.org/",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -29,7 +29,7 @@ class InAppWebViewOnLoadHttpErrorTestState extends WidgetTestState {
child: InAppWebView(
initialUrl: "https://google.com/404",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -29,7 +29,7 @@ class InAppWebViewOnLoadResourceCustomSchemeTestState extends WidgetTestState {
child: InAppWebView(
initialFile: "test_assets/in_app_webview_on_load_resource_custom_scheme_test.html",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,

View File

@ -34,7 +34,7 @@ class InAppWebViewOnLoadResourceTestState extends WidgetTestState {
child: InAppWebView(
initialFile: "test_assets/in_app_webview_on_load_resource_test.html",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,

View File

@ -28,7 +28,7 @@ class InAppWebViewOnNavigationStateChangeTestState extends WidgetTestState {
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -29,7 +29,7 @@ class InAppWebViewOnProgressChangedTestState extends WidgetTestState {
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -30,7 +30,7 @@ class InAppWebViewOnReceivedHttpAuthRequestTestState extends WidgetTestState {
child: InAppWebView(
initialUrl: "http://${environment["NODE_SERVER_IP"]}:8081/",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -31,7 +31,7 @@ class InAppWebViewOnSafeBrowsingHitTestState extends WidgetTestState {
child: InAppWebView(
initialUrl: (Platform.isAndroid) ? "chrome://safe-browsing/match?type=malware" : "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
// if I set javaScriptEnabled to true, it will crash!
javaScriptEnabled: false,

View File

@ -30,7 +30,7 @@ class InAppWebViewOnScrollChangedTestState extends WidgetTestState {
child: InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -28,7 +28,7 @@ class InAppWebViewShouldOverrideUrlLoadingTestState extends WidgetTestState {
child: InAppWebView(
initialFile: "test_assets/in_app_webview_initial_file_test.html",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true,

View File

@ -30,7 +30,7 @@ class InAppWebViewSslRequestTestState extends WidgetTestState {
child: InAppWebView(
initialUrl: "https://${environment["NODE_SERVER_IP"]}:4433/",
initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
clearCache: true,
debuggingEnabled: true

View File

@ -26,6 +26,7 @@
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/ios/Flutter/App.framework/flutter_assets/packages" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/App.framework/flutter_assets/packages" />
<excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.pub" />

View File

@ -41,7 +41,8 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
let uuidFallback: String = arguments!["uuidFallback"] as! String
let headersFallback = arguments!["headersFallback"] as! [String: String]
let optionsFallback = arguments!["optionsFallback"] as! [String: Any?]
open(uuid: uuid, url: url, options: options, uuidFallback: uuidFallback, headersFallback: headersFallback, optionsFallback: optionsFallback, result: result)
let menuItemList = arguments!["menuItemList"] as! [[String: Any]]
open(uuid: uuid, url: url, options: options, uuidFallback: uuidFallback, headersFallback: headersFallback, optionsFallback: optionsFallback, menuItemList: menuItemList, result: result)
break
default:
result(FlutterMethodNotImplemented)
@ -49,7 +50,7 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
}
}
public func open(uuid: String, url: String, options: [String: Any?], uuidFallback: String?, headersFallback: [String: String], optionsFallback: [String: Any?], result: @escaping FlutterResult) {
public func open(uuid: String, url: String, options: [String: Any?], uuidFallback: String?, headersFallback: [String: String], optionsFallback: [String: Any?], menuItemList: [[String: Any]], result: @escaping FlutterResult) {
let absoluteUrl = URL(string: url)!.absoluteURL
if self.previousStatusBarStyle == -1 {
@ -85,10 +86,12 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
}
safari.uuid = uuid
safari.menuItemList = menuItemList
safari.prepareMethodChannel()
safari.delegate = safari
safari.tmpWindow = tmpWindow
safari.safariOptions = safariOptions
safari.prepareSafariBrowser()
tmpController.present(safari, animated: true) {
result(true)

View File

@ -12,22 +12,27 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
private weak var registrar: FlutterPluginRegistrar?
var webView: InAppWebView?
var viewId: Int64 = 0
var viewId: Any = 0
var channel: FlutterMethodChannel?
var myView: UIView?
init(registrar: FlutterPluginRegistrar, withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: NSDictionary) {
init(registrar: FlutterPluginRegistrar, withFrame frame: CGRect, viewIdentifier viewId: Any, arguments args: NSDictionary) {
super.init()
self.registrar = registrar
self.viewId = viewId
myView = UIView(frame: frame)
let channelName = "com.pichillilorenzo/flutter_inappwebview_" + String(viewId)
var channelName = ""
if let id = viewId as? Int64 {
channelName = "com.pichillilorenzo/flutter_inappwebview_" + String(id)
} else if let id = viewId as? String {
channelName = "com.pichillilorenzo/flutter_inappwebview_" + id
}
channel = FlutterMethodChannel(name: channelName, binaryMessenger: registrar.messenger())
channel!.setMethodCallHandler(LeakAvoider(delegate: self).handle)
myView = UIView(frame: frame)
let initialUrl = args["initialUrl"] as? String
let initialFile = args["initialFile"] as? String
let initialData = args["initialData"] as? [String: String]
@ -74,6 +79,20 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
}
}
load(initialUrl: initialUrl, initialFile: initialFile, initialData: initialData, initialHeaders: initialHeaders)
if (frame.isEmpty && viewId is String) {
/// Note: The WKWebView behaves very unreliable when rendering offscreen
/// on a device. This is especially true with JavaScript, which simply
/// won't be executed sometimes.
/// Therefore, I decided to add this very ugly hack where the rendering
/// webview will be added to the view hierarchy (between the
/// rootViewController's view and the key window).
self.myView!.alpha = 0.01
UIApplication.shared.keyWindow!.insertSubview(self.myView!, at: 0)
let arguments: [String: Any] = ["uuid": viewId]
channel!.invokeMethod("onHeadlessWebViewCreated", arguments: arguments)
}
}
deinit {

View File

@ -0,0 +1,65 @@
//
// HeadlessInAppWebViewManager.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 10/05/2020.
//
import Foundation
import Flutter
import UIKit
import WebKit
import Foundation
import AVFoundation
public class HeadlessInAppWebViewManager: NSObject, FlutterPlugin {
static var registrar: FlutterPluginRegistrar?
static var channel: FlutterMethodChannel?
var flutterWebViews: [String: FlutterWebViewController] = [:]
public static func register(with registrar: FlutterPluginRegistrar) {
}
init(registrar: FlutterPluginRegistrar) {
super.init()
HeadlessInAppWebViewManager.registrar = registrar
HeadlessInAppWebViewManager.channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_headless_inappwebview", binaryMessenger: registrar.messenger())
registrar.addMethodCallDelegate(self, channel: HeadlessInAppWebViewManager.channel!)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? NSDictionary
let uuid: String = arguments!["uuid"] as! String
switch call.method {
case "createHeadlessWebView":
let params = arguments!["params"] as! [String: Any?]
createHeadlessWebView(uuid: uuid, params: params)
result(true)
break
case "disposeHeadlessWebView":
disposeHeadlessWebView(uuid: uuid)
result(true)
break
default:
result(FlutterMethodNotImplemented)
break
}
}
public func createHeadlessWebView(uuid: String, params: [String: Any?]) {
let controller = FlutterWebViewController(registrar: HeadlessInAppWebViewManager.registrar!,
withFrame: CGRect.zero,
viewIdentifier: uuid,
arguments: params as NSDictionary)
flutterWebViews[uuid] = controller
}
public func disposeHeadlessWebView(uuid: String) {
if let _ = flutterWebViews[uuid] {
flutterWebViews.removeValue(forKey: uuid)
}
}
}

View File

@ -15,6 +15,7 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
var tmpWindow: UIWindow?
var safariOptions: SafariBrowserOptions?
var uuid: String = ""
var menuItemList: [[String: Any]] = []
public static func register(with registrar: FlutterPluginRegistrar) {
@ -30,11 +31,20 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? NSDictionary
switch call.method {
case "close":
close()
break
default:
result(FlutterMethodNotImplemented)
break
}
}
public override func viewWillAppear(_ animated: Bool) {
prepareSafariBrowser()
// prepareSafariBrowser()
super.viewWillAppear(animated)
onChromeSafariBrowserOpened()
}
@ -83,23 +93,25 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
}
public func safariViewController(_ controller: SFSafariViewController, activityItemsFor URL: URL, title: String?) -> [UIActivity] {
// print("activityItemsFor")
// print(URL)
// print(title)
return []
var uiActivities: [UIActivity] = []
menuItemList.forEach { (menuItem) in
let activity = CustomUIActivity(uuid: uuid, id: menuItem["id"] as! Int64, url: URL, title: title, label: menuItem["label"] as? String, type: nil, image: nil)
uiActivities.append(activity)
}
return uiActivities
}
public func safariViewController(_ controller: SFSafariViewController, excludedActivityTypesFor URL: URL, title: String?) -> [UIActivity.ActivityType] {
//
// public func safariViewController(_ controller: SFSafariViewController, excludedActivityTypesFor URL: URL, title: String?) -> [UIActivity.ActivityType] {
// print("excludedActivityTypesFor")
// print(URL)
// print(title)
return []
}
public func safariViewController(_ controller: SFSafariViewController, initialLoadDidRedirectTo URL: URL) {
// return []
// }
//
// public func safariViewController(_ controller: SFSafariViewController, initialLoadDidRedirectTo URL: URL) {
// print("initialLoadDidRedirectTo")
// print(URL)
}
// }
public func onChromeSafariBrowserOpened() {
channel!.invokeMethod("onChromeSafariBrowserOpened", arguments: [])
@ -146,3 +158,54 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
return hexInt
}
}
class CustomUIActivity : UIActivity {
var uuid: String
var id: Int64
var url: URL
var title: String?
var type: UIActivity.ActivityType?
var label: String?
var image: UIImage?
init(uuid: String, id: Int64, url: URL, title: String?, label: String?, type: UIActivity.ActivityType?, image: UIImage?) {
self.uuid = uuid
self.id = id
self.url = url
self.title = title
self.label = label
self.type = type
self.image = image
}
override class var activityCategory: UIActivity.Category {
return .action
}
override var activityType: UIActivity.ActivityType? {
return type
}
override var activityTitle: String? {
return label
}
override var activityImage: UIImage? {
return image
}
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
return true
}
override func perform() {
let channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_chromesafaribrowser_" + uuid, binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
let arguments: [String: Any?] = [
"url": url.absoluteString,
"title": title,
"id": id,
]
channel.invokeMethod("onChromeSafariBrowserMenuItemActionPerform", arguments: arguments)
}
}

View File

@ -31,6 +31,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
var myWebStorageManager: Any?
var credentialDatabase: CredentialDatabase?
var inAppBrowserManager: InAppBrowserManager?
var headlessInAppWebViewManager: HeadlessInAppWebViewManager?
var chromeSafariBrowserManager: ChromeSafariBrowserManager?
var webViewControllers: [String: InAppBrowserWebViewController?] = [:]
@ -46,6 +47,7 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
registrar.register(FlutterWebViewFactory(registrar: registrar) as FlutterPlatformViewFactory, withId: "com.pichillilorenzo/flutter_inappwebview")
inAppBrowserManager = InAppBrowserManager(registrar: registrar)
headlessInAppWebViewManager = HeadlessInAppWebViewManager(registrar: registrar)
chromeSafariBrowserManager = ChromeSafariBrowserManager(registrar: registrar)
inAppWebViewStatic = InAppWebViewStatic(registrar: registrar)
if #available(iOS 11.0, *) {

View File

@ -22,6 +22,9 @@
library flutter_inappwebview;
export 'src/types.dart';
export 'src/webview.dart';
export 'src/in_app_webview_controller.dart';
export 'src/headless_in_app_webview.dart';
export 'src/in_app_webview.dart';
export 'src/in_app_browser.dart';
export 'src/cookie_manager.dart';

View File

@ -1,5 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
@ -7,8 +7,6 @@ import 'package:flutter/services.dart';
import 'types.dart';
import 'in_app_browser.dart';
///ChromeSafariBrowser class.
///
///This class uses native [Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android
///and [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS.
///
@ -16,6 +14,7 @@ import 'in_app_browser.dart';
class ChromeSafariBrowser {
String uuid;
InAppBrowser browserFallback;
Map<int, ChromeSafariBrowserMenuItem> _menuItems = new HashMap();
bool _isOpened = false;
MethodChannel _channel;
static const MethodChannel _sharedChannel = const MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser');
@ -42,6 +41,12 @@ class ChromeSafariBrowser {
onClosed();
this._isOpened = false;
break;
case "onChromeSafariBrowserMenuItemActionPerform":
String url = call.arguments["url"];
String title = call.arguments["title"];
int id = call.arguments["id"].toInt();
this._menuItems[id].action(url, title);
break;
default:
throw UnimplementedError("Unimplemented ${call.method} method");
}
@ -64,49 +69,45 @@ class ChromeSafariBrowser {
assert(url != null && url.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $url!');
Map<String, dynamic> optionsMap = {};
if (Platform.isAndroid)
optionsMap.addAll(options.android?.toMap() ?? {});
else if (Platform.isIOS)
optionsMap.addAll(options.ios?.toMap() ?? {});
Map<String, dynamic> optionsFallbackMap = {};
if (optionsFallback != null) {
optionsFallbackMap
.addAll(optionsFallback.crossPlatform?.toMap() ?? {});
optionsFallbackMap.addAll(optionsFallback
.inAppWebViewWidgetOptions?.crossPlatform
?.toMap() ??
{});
if (Platform.isAndroid) {
optionsFallbackMap
.addAll(optionsFallback.android?.toMap() ?? {});
optionsFallbackMap.addAll(optionsFallback
.inAppWebViewWidgetOptions?.android
?.toMap() ??
{});
} else if (Platform.isIOS) {
optionsFallbackMap
.addAll(optionsFallback.ios?.toMap() ?? {});
optionsFallbackMap.addAll(optionsFallback
.inAppWebViewWidgetOptions?.ios
?.toMap() ??
{});
}
}
List<Map<String, dynamic>> menuItemList = new List();
_menuItems.forEach((key, value) {
menuItemList.add({
"id": value.id,
"label": value.label
});
});
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('url', () => url);
args.putIfAbsent('options', () => optionsMap);
args.putIfAbsent('options', () => options?.toMap() ?? {});
args.putIfAbsent('uuidFallback',
() => (browserFallback != null) ? browserFallback.uuid : '');
args.putIfAbsent('headersFallback', () => headersFallback);
args.putIfAbsent('optionsFallback', () => optionsFallbackMap);
args.putIfAbsent('optionsFallback', () => optionsFallback?.toMap() ?? {});
args.putIfAbsent('menuItemList', () => menuItemList);
await _sharedChannel.invokeMethod('open', args);
this._isOpened = true;
}
///Closes the [ChromeSafariBrowser] instance.
Future<void> close() async {
Map<String, dynamic> args = <String, dynamic>{};
await _channel.invokeMethod("close", args);
}
///Adds a [ChromeSafariBrowserMenuItem] to the menu.
void addMenuItem(ChromeSafariBrowserMenuItem menuItem) {
this._menuItems[menuItem.id] = menuItem;
}
///Adds a list of [ChromeSafariBrowserMenuItem] to the menu.
void addMenuItems(List<ChromeSafariBrowserMenuItem> menuItems) {
menuItems.forEach((menuItem) {
this._menuItems[menuItem.id] = menuItem;
});
}
///Event fires when the [ChromeSafariBrowser] is opened.
void onOpened() {}
@ -137,3 +138,11 @@ class ChromeSafariBrowser {
}
}
}
class ChromeSafariBrowserMenuItem {
int id;
String label;
final void Function(String url, String title) action;
ChromeSafariBrowserMenuItem({@required this.id, @required this.label, @required this.action});
}

View File

@ -1,7 +1,7 @@
import 'package:flutter/foundation.dart';
import 'types.dart';
///ContentBlocker class represents a set of rules to use block content in the browser window.
///Class that represents a set of rules to use block content in the browser window.
///
///On iOS, it uses [WKContentRuleListStore](https://developer.apple.com/documentation/webkit/wkcontentruleliststore).
///On Android, it uses a custom implementation because such functionality doesn't exist.

View File

@ -5,7 +5,7 @@ import 'package:flutter/services.dart';
import 'types.dart';
///CookieManager class implements a singleton object (shared instance) which manages the cookies used by WebView instances.
///Class that implements a singleton object (shared instance) which manages the cookies used by WebView instances.
///
///**NOTE for iOS**: available from iOS 11.0+.
class CookieManager {

View File

@ -0,0 +1,264 @@
import 'package:flutter/services.dart';
import 'types.dart';
import 'webview.dart';
import 'in_app_webview_controller.dart';
///Class that represents a WebView in headless mode.
///It can be used to run a WebView in background without attaching an `InAppWebView` to the widget tree.
///
///Remember to dispose it when you don't need it anymore.
class HeadlessInAppWebView implements WebView {
String uuid;
bool _isDisposed = true;
static const MethodChannel _sharedChannel = const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview');
///WebView Controller that can be used to access the [InAppWebViewController] API.
InAppWebViewController webViewController;
HeadlessInAppWebView(
{this.onWebViewCreated,
this.onLoadStart,
this.onLoadStop,
this.onLoadError,
this.onLoadHttpError,
this.onProgressChanged,
this.onConsoleMessage,
this.shouldOverrideUrlLoading,
this.onLoadResource,
this.onScrollChanged,
this.onDownloadStart,
this.onLoadResourceCustomScheme,
this.onCreateWindow,
this.onJsAlert,
this.onJsConfirm,
this.onJsPrompt,
this.onReceivedHttpAuthRequest,
this.onReceivedServerTrustAuthRequest,
this.onReceivedClientCertRequest,
this.onFindResultReceived,
this.shouldInterceptAjaxRequest,
this.onAjaxReadyStateChange,
this.onAjaxProgress,
this.shouldInterceptFetchRequest,
this.onUpdateVisitedHistory,
this.onPrint,
this.onLongPressHitTestResult,
this.androidOnSafeBrowsingHit,
this.androidOnPermissionRequest,
this.androidOnGeolocationPermissionsShowPrompt,
this.androidOnGeolocationPermissionsHidePrompt,
this.iosOnWebContentProcessDidTerminate,
this.iosOnDidCommit,
this.iosOnDidReceiveServerRedirectForProvisionalNavigation,
this.initialUrl,
this.initialFile,
this.initialData,
this.initialHeaders,
this.initialOptions}) {
uuid = uuidGenerator.v4();
webViewController = new InAppWebViewController(uuid, this);
}
Future<dynamic> handleMethod(MethodCall call) async {
switch (call.method) {
case "onHeadlessWebViewCreated":
onWebViewCreated(webViewController);
break;
default:
return webViewController.handleMethod(call);
}
}
///Runs the headless WebView.
Future<void> run() async {
if (!_isDisposed) {
return;
}
_isDisposed = false;
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('params', () => <String, dynamic>{
'initialUrl': '${Uri.parse(this.initialUrl)}',
'initialFile': this.initialFile,
'initialData': this.initialData?.toMap(),
'initialHeaders': this.initialHeaders,
'initialOptions': this.initialOptions?.toMap()
});
await _sharedChannel.invokeMethod('createHeadlessWebView', args);
}
///Disposes the headless WebView.
Future<void> dispose() async {
if (_isDisposed) {
return;
}
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
await _sharedChannel.invokeMethod('disposeHeadlessWebView', args);
_isDisposed = true;
}
@override
final Future<void> Function(InAppWebViewController controller)
androidOnGeolocationPermissionsHidePrompt;
@override
final Future<GeolocationPermissionShowPromptResponse> Function(
InAppWebViewController controller, String origin)
androidOnGeolocationPermissionsShowPrompt;
@override
final Future<PermissionRequestResponse> Function(
InAppWebViewController controller,
String origin,
List<String> resources) androidOnPermissionRequest;
@override
final Future<SafeBrowsingResponse> Function(InAppWebViewController controller,
String url, SafeBrowsingThreat threatType) androidOnSafeBrowsingHit;
@override
final InAppWebViewInitialData initialData;
@override
final String initialFile;
@override
final Map<String, String> initialHeaders;
@override
final InAppWebViewGroupOptions initialOptions;
@override
final String initialUrl;
@override
final Future<void> Function(InAppWebViewController controller) iosOnDidCommit;
@override
final Future<void> Function(InAppWebViewController controller)
iosOnDidReceiveServerRedirectForProvisionalNavigation;
@override
final Future<void> Function(InAppWebViewController controller)
iosOnWebContentProcessDidTerminate;
@override
final Future<AjaxRequestAction> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)
onAjaxProgress;
@override
final Future<AjaxRequestAction> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)
onAjaxReadyStateChange;
@override
final void Function(
InAppWebViewController controller, ConsoleMessage consoleMessage)
onConsoleMessage;
@override
final void Function(InAppWebViewController controller,
OnCreateWindowRequest onCreateWindowRequest) onCreateWindow;
@override
final void Function(InAppWebViewController controller, String url)
onDownloadStart;
@override
final void Function(InAppWebViewController controller, int activeMatchOrdinal,
int numberOfMatches, bool isDoneCounting) onFindResultReceived;
@override
final Future<JsAlertResponse> Function(
InAppWebViewController controller, String message) onJsAlert;
@override
final Future<JsConfirmResponse> Function(
InAppWebViewController controller, String message) onJsConfirm;
@override
final Future<JsPromptResponse> Function(InAppWebViewController controller,
String message, String defaultValue) onJsPrompt;
@override
final void Function(InAppWebViewController controller, String url, int code,
String message) onLoadError;
@override
final void Function(InAppWebViewController controller, String url,
int statusCode, String description) onLoadHttpError;
@override
final void Function(
InAppWebViewController controller, LoadedResource resource)
onLoadResource;
@override
final Future<CustomSchemeResponse> Function(
InAppWebViewController controller, String scheme, String url)
onLoadResourceCustomScheme;
@override
final void Function(InAppWebViewController controller, String url)
onLoadStart;
@override
final void Function(InAppWebViewController controller, String url) onLoadStop;
@override
final void Function(InAppWebViewController controller,
LongPressHitTestResult hitTestResult) onLongPressHitTestResult;
@override
final void Function(InAppWebViewController controller, String url) onPrint;
@override
final void Function(InAppWebViewController controller, int progress)
onProgressChanged;
@override
final Future<ClientCertResponse> Function(
InAppWebViewController controller, ClientCertChallenge challenge)
onReceivedClientCertRequest;
@override
final Future<HttpAuthResponse> Function(
InAppWebViewController controller, HttpAuthChallenge challenge)
onReceivedHttpAuthRequest;
@override
final Future<ServerTrustAuthResponse> Function(
InAppWebViewController controller, ServerTrustChallenge challenge)
onReceivedServerTrustAuthRequest;
@override
final void Function(InAppWebViewController controller, int x, int y)
onScrollChanged;
@override
final void Function(
InAppWebViewController controller, String url, bool androidIsReload)
onUpdateVisitedHistory;
@override
final void Function(InAppWebViewController controller) onWebViewCreated;
@override
final Future<AjaxRequest> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)
shouldInterceptAjaxRequest;
@override
final Future<FetchRequest> Function(
InAppWebViewController controller, FetchRequest fetchRequest)
shouldInterceptFetchRequest;
@override
final Future<ShouldOverrideUrlLoadingAction> Function(
InAppWebViewController controller,
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest)
shouldOverrideUrlLoading;
}

View File

@ -5,7 +5,7 @@ import 'package:flutter/foundation.dart';
import 'types.dart';
import 'package:flutter/services.dart';
///HttpAuthCredentialDatabase class implements a singleton object (shared instance) which manages the shared HTTP auth credentials cache.
///Class that implements a singleton object (shared instance) which manages the shared HTTP auth credentials cache.
///On iOS, this class uses the [URLCredentialStorage](https://developer.apple.com/documentation/foundation/urlcredentialstorage) class.
///On Android, this class has a custom implementation using `android.database.sqlite.SQLiteDatabase` because [WebViewDatabase](https://developer.android.com/reference/android/webkit/WebViewDatabase)
///doesn't offer the same functionalities as iOS `URLCredentialStorage`.

View File

@ -4,14 +4,13 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'in_app_webview_controller.dart';
import 'webview_options.dart';
import 'types.dart';
import 'in_app_webview.dart' show InAppWebViewController;
///InAppBrowser class. [webViewController] can be used to access the [InAppWebView] API.
///
///This class uses the native WebView of the platform.
///The [webViewController] field can be used to access the [InAppWebViewController] API.
class InAppBrowser {
String uuid;
Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap =
@ -20,7 +19,7 @@ class InAppBrowser {
MethodChannel _channel;
static const MethodChannel _sharedChannel = const MethodChannel('com.pichillilorenzo/flutter_inappbrowser');
/// WebView Controller that can be used to access the [InAppWebView] API.
/// WebView Controller that can be used to access the [InAppWebViewController] API.
InAppWebViewController webViewController;
///
@ -63,29 +62,11 @@ class InAppBrowser {
assert(url != null && url.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $url!');
Map<String, dynamic> optionsMap = {};
optionsMap.addAll(options.crossPlatform?.toMap() ?? {});
optionsMap.addAll(
options.inAppWebViewWidgetOptions?.crossPlatform?.toMap() ?? {});
if (Platform.isAndroid) {
optionsMap.addAll(options.android?.toMap() ?? {});
optionsMap.addAll(options
.inAppWebViewWidgetOptions?.android
?.toMap() ??
{});
} else if (Platform.isIOS) {
optionsMap.addAll(options.ios?.toMap() ?? {});
optionsMap.addAll(
options.inAppWebViewWidgetOptions?.ios?.toMap() ??
{});
}
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('url', () => url);
args.putIfAbsent('headers', () => headers);
args.putIfAbsent('options', () => optionsMap);
args.putIfAbsent('options', () => options?.toMap() ?? {});
await _sharedChannel.invokeMethod('openUrl', args);
}
@ -129,29 +110,13 @@ class InAppBrowser {
assert(assetFilePath != null && assetFilePath.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $assetFilePath!');
Map<String, dynamic> optionsMap = {};
optionsMap.addAll(options.crossPlatform?.toMap() ?? {});
optionsMap.addAll(
options.inAppWebViewWidgetOptions?.crossPlatform?.toMap() ?? {});
if (Platform.isAndroid) {
optionsMap.addAll(options.android?.toMap() ?? {});
optionsMap.addAll(options
.inAppWebViewWidgetOptions?.android
?.toMap() ??
{});
} else if (Platform.isIOS) {
optionsMap.addAll(options.ios?.toMap() ?? {});
optionsMap.addAll(
options.inAppWebViewWidgetOptions?.ios?.toMap() ??
{});
}
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('url', () => assetFilePath);
args.putIfAbsent('headers', () => headers);
args.putIfAbsent('options', () => optionsMap);
args.putIfAbsent('options', () => options?.toMap() ?? {});
await _sharedChannel.invokeMethod('openFile', args);
}
@ -173,27 +138,9 @@ class InAppBrowser {
InAppBrowserClassOptions options}) async {
assert(data != null);
Map<String, dynamic> optionsMap = {};
optionsMap.addAll(options.crossPlatform?.toMap() ?? {});
optionsMap.addAll(
options.inAppWebViewWidgetOptions?.crossPlatform?.toMap() ?? {});
if (Platform.isAndroid) {
optionsMap.addAll(options.android?.toMap() ?? {});
optionsMap.addAll(options
.inAppWebViewWidgetOptions?.android
?.toMap() ??
{});
} else if (Platform.isIOS) {
optionsMap.addAll(options.ios?.toMap() ?? {});
optionsMap.addAll(
options.inAppWebViewWidgetOptions?.ios?.toMap() ??
{});
}
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('options', () => optionsMap);
args.putIfAbsent('options', () => options?.toMap() ?? {});
args.putIfAbsent('data', () => data);
args.putIfAbsent('mimeType', () => mimeType);
args.putIfAbsent('encoding', () => encoding);
@ -243,26 +190,8 @@ class InAppBrowser {
Future<void> setOptions({@required InAppBrowserClassOptions options}) async {
this.throwIsNotOpened();
Map<String, dynamic> optionsMap = {};
optionsMap.addAll(options.crossPlatform?.toMap() ?? {});
optionsMap.addAll(
options.inAppWebViewWidgetOptions?.crossPlatform?.toMap() ?? {});
if (Platform.isAndroid) {
optionsMap.addAll(options.android?.toMap() ?? {});
optionsMap.addAll(options
.inAppWebViewWidgetOptions?.android
?.toMap() ??
{});
} else if (Platform.isIOS) {
optionsMap.addAll(options.ios?.toMap() ?? {});
optionsMap.addAll(
options.inAppWebViewWidgetOptions?.ios?.toMap() ??
{});
}
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('options', () => optionsMap);
args.putIfAbsent('options', () => options?.toMap() ?? {});
await _channel.invokeMethod('setOptions', args);
}
@ -279,19 +208,19 @@ class InAppBrowser {
options = options.cast<String, dynamic>();
inAppBrowserClassOptions.crossPlatform =
InAppBrowserOptions.fromMap(options);
inAppBrowserClassOptions.inAppWebViewWidgetOptions = InAppWebViewWidgetOptions();
inAppBrowserClassOptions.inAppWebViewWidgetOptions.crossPlatform =
inAppBrowserClassOptions.inAppWebViewGroupOptions = InAppWebViewGroupOptions();
inAppBrowserClassOptions.inAppWebViewGroupOptions.crossPlatform =
InAppWebViewOptions.fromMap(options);
if (Platform.isAndroid) {
inAppBrowserClassOptions.android =
AndroidInAppBrowserOptions.fromMap(options);
inAppBrowserClassOptions
.inAppWebViewWidgetOptions.android =
.inAppWebViewGroupOptions.android =
AndroidInAppWebViewOptions.fromMap(options);
} else if (Platform.isIOS) {
inAppBrowserClassOptions.ios =
IOSInAppBrowserOptions.fromMap(options);
inAppBrowserClassOptions.inAppWebViewWidgetOptions
inAppBrowserClassOptions.inAppWebViewGroupOptions
.ios = IOSInAppWebViewOptions.fromMap(options);
}
}

View File

@ -4,8 +4,6 @@ import 'dart:async';
import 'package:flutter/services.dart' show rootBundle;
import 'package:mime/mime.dart';
///InAppLocalhostServer class.
///
///This class allows you to create a simple server on `http://localhost:[port]/` in order to be able to load your assets file on a server. The default [port] value is `8080`.
class InAppLocalhostServer {
HttpServer _server;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,7 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:uuid/uuid.dart';
import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart';
import 'webview_options.dart';
@ -183,8 +182,6 @@ class ConsoleMessage {
{this.message = "", this.messageLevel = ConsoleMessageLevel.LOG});
}
///WebHistory class.
///
///This class contains a snapshot of the current back/forward list for a WebView.
class WebHistory {
///List of all [WebHistoryItem]s.
@ -196,8 +193,6 @@ class WebHistory {
WebHistory({this.list, this.currentIndex});
}
///WebHistoryItem class.
///
///A convenience class for accessing fields in an entry in the back/forward list of a WebView. Each WebHistoryItem is a snapshot of the requested history item.
class WebHistoryItem {
///Original url of this history item.
@ -219,8 +214,6 @@ class WebHistoryItem {
{this.originalUrl, this.title, this.url, this.index, this.offset});
}
///GeolocationPermissionPromptResponse class.
///
///Class used by the host application to set the Geolocation permission state for an origin during the [androidOnGeolocationPermissionsShowPrompt] event.
class GeolocationPermissionShowPromptResponse {
///The origin for which permissions are set.
@ -240,7 +233,7 @@ class GeolocationPermissionShowPromptResponse {
}
}
///JsAlertResponseAction class used by [JsAlertResponse] class.
///Class used by [JsAlertResponse] class.
class JsAlertResponseAction {
final int _value;
const JsAlertResponseAction._internal(this._value);
@ -254,7 +247,7 @@ class JsAlertResponseAction {
int get hashCode => _value.hashCode;
}
///JsAlertResponse class represents the response used by the [onJsAlert] event to control a JavaScript alert dialog.
///Class represents the response used by the [onJsAlert] event to control a JavaScript alert dialog.
class JsAlertResponse {
///Message to be displayed in the window.
String message;
@ -284,7 +277,7 @@ class JsAlertResponse {
}
}
///JsConfirmResponseAction class used by [JsConfirmResponse] class.
///Class used by [JsConfirmResponse] class.
class JsConfirmResponseAction {
final int _value;
const JsConfirmResponseAction._internal(this._value);
@ -299,7 +292,7 @@ class JsConfirmResponseAction {
int get hashCode => _value.hashCode;
}
///JsConfirmResponse class represents the response used by the [onJsConfirm] event to control a JavaScript confirm dialog.
///Class that represents the response used by the [onJsConfirm] event to control a JavaScript confirm dialog.
class JsConfirmResponse {
///Message to be displayed in the window.
String message;
@ -334,7 +327,7 @@ class JsConfirmResponse {
}
}
///JsPromptResponseAction class used by [JsPromptResponse] class.
///Class used by [JsPromptResponse] class.
class JsPromptResponseAction {
final int _value;
const JsPromptResponseAction._internal(this._value);
@ -349,7 +342,7 @@ class JsPromptResponseAction {
int get hashCode => _value.hashCode;
}
///JsPromptResponse class represents the response used by the [onJsPrompt] event to control a JavaScript prompt dialog.
///Class that represents the response used by the [onJsPrompt] event to control a JavaScript prompt dialog.
class JsPromptResponse {
///Message to be displayed in the window.
String message;
@ -394,7 +387,7 @@ class JsPromptResponse {
}
}
///SafeBrowsingThreat class represents the reason the resource was caught by Safe Browsing.
///Class that represents the reason the resource was caught by Safe Browsing.
class SafeBrowsingThreat {
final int _value;
const SafeBrowsingThreat._internal(this._value);
@ -438,7 +431,7 @@ class SafeBrowsingThreat {
int get hashCode => _value.hashCode;
}
///SafeBrowsingResponseAction class used by [SafeBrowsingResponse] class.
///Class used by [SafeBrowsingResponse] class.
class SafeBrowsingResponseAction {
final int _value;
const SafeBrowsingResponseAction._internal(this._value);
@ -460,7 +453,7 @@ class SafeBrowsingResponseAction {
int get hashCode => _value.hashCode;
}
///SafeBrowsingResponse class represents the response used by the [androidOnSafeBrowsingHit] event.
///Class that represents the response used by the [androidOnSafeBrowsingHit] event.
///It is used to indicate an action to take when hitting a malicious URL.
class SafeBrowsingResponse {
///If reporting is enabled, all reports will be sent according to the privacy policy referenced by [InAppWebViewController.androidGetSafeBrowsingPrivacyPolicyUrl].
@ -478,7 +471,7 @@ class SafeBrowsingResponse {
}
}
///HttpAuthResponseAction class used by [HttpAuthResponse] class.
///Class used by [HttpAuthResponse] class.
class HttpAuthResponseAction {
final int _value;
const HttpAuthResponseAction._internal(this._value);
@ -500,7 +493,7 @@ class HttpAuthResponseAction {
int get hashCode => _value.hashCode;
}
///HttpAuthResponse class represents the response used by the [onReceivedHttpAuthRequest] event.
///Class that represents the response used by the [onReceivedHttpAuthRequest] event.
class HttpAuthResponse {
///Represents the username used for the authentication if the [action] corresponds to [HttpAuthResponseAction.PROCEED]
String username;
@ -530,7 +523,7 @@ class HttpAuthResponse {
}
}
///HttpAuthChallenge class represents the challenge of the [onReceivedHttpAuthRequest] event.
///Class that represents the challenge of the [onReceivedHttpAuthRequest] event.
///It provides all the information about the challenge.
class HttpAuthChallenge {
///A count of previous failed authentication attempts.
@ -544,7 +537,7 @@ class HttpAuthChallenge {
: assert(previousFailureCount != null && protectionSpace != null);
}
///ProtectionSpace class represents a protection space requiring authentication.
///Class that represents a protection space requiring authentication.
class ProtectionSpace {
///The hostname of the server.
String host;
@ -565,7 +558,7 @@ class ProtectionSpace {
: assert(host != null && protocol != null);
}
///HttpAuthCredential represents the credentials of an http authentication.
///Class that represents the credentials of an http authentication.
///It is used by the [HttpAuthCredentialDatabase] class.
class HttpAuthCredential {
///Represents the username.
@ -578,7 +571,7 @@ class HttpAuthCredential {
: assert(username != null && password != null);
}
///ServerTrustAuthResponseAction class used by [ServerTrustAuthResponse] class.
///Class used by [ServerTrustAuthResponse] class.
class ServerTrustAuthResponseAction {
final int _value;
const ServerTrustAuthResponseAction._internal(this._value);
@ -608,7 +601,7 @@ class ServerTrustAuthResponse {
}
}
///ServerTrustChallenge class represents the challenge of the [onReceivedServerTrustAuthRequest] event.
///Class that represents the challenge of the [onReceivedServerTrustAuthRequest] event.
///It provides all the information about the challenge.
class ServerTrustChallenge {
///The protection space requiring authentication.
@ -635,7 +628,7 @@ class ServerTrustChallenge {
: assert(protectionSpace != null && error != null);
}
///ClientCertResponseAction class used by [ClientCertResponse] class.
///Class used by [ClientCertResponse] class.
class ClientCertResponseAction {
final int _value;
const ClientCertResponseAction._internal(this._value);
@ -656,7 +649,7 @@ class ClientCertResponseAction {
int get hashCode => _value.hashCode;
}
///ClientCertResponse class represents the response used by the [onReceivedClientCertRequest] event.
///Class that represents the response used by the [onReceivedClientCertRequest] event.
class ClientCertResponse {
///The file path of the certificate to use.
String certificatePath;
@ -689,7 +682,7 @@ class ClientCertResponse {
}
}
///ClientCertChallenge class represents the challenge of the [onReceivedClientCertRequest] event.
///Class that represents the challenge of the [onReceivedClientCertRequest] event.
///It provides all the information about the challenge.
class ClientCertChallenge {
///The protection space requiring authentication.
@ -699,7 +692,7 @@ class ClientCertChallenge {
: assert(protectionSpace != null);
}
///Favicon class represents a favicon of a website. It is used by [InAppWebViewController.getFavicons] method.
///Class that represents a favicon of a website. It is used by [InAppWebViewController.getFavicons] method.
class Favicon {
///The url of the favicon image.
String url;
@ -721,7 +714,7 @@ class Favicon {
}
}
///AndroidCacheMode class represents an Android-specific class used to override the way the cache is used.
///Class that represents an Android-specific class used to override the way the cache is used.
class AndroidCacheMode {
final int _value;
const AndroidCacheMode._internal(this._value);
@ -768,7 +761,7 @@ class AndroidCacheMode {
int get hashCode => _value.hashCode;
}
///AndroidActionModeMenuItem class represents an Android-specific class used to disable the action mode menu items.
///Class that represents an Android-specific class used to disable the action mode menu items.
///
///**NOTE**: available on Android 24+.
class AndroidActionModeMenuItem {
@ -818,7 +811,7 @@ class AndroidActionModeMenuItem {
int get hashCode => _value.hashCode;
}
///AndroidForceDark class represents an Android-specific class used to indicate the force dark mode.
///Class that represents an Android-specific class used to indicate the force dark mode.
///
///**NOTE**: available on Android 29+.
class AndroidForceDark {
@ -861,7 +854,7 @@ class AndroidForceDark {
int get hashCode => _value.hashCode;
}
///AndroidLayoutAlgorithm class represents an Android-specific class used to set the underlying layout algorithm.
///Class that represents an Android-specific class used to set the underlying layout algorithm.
class AndroidLayoutAlgorithm {
final String _value;
const AndroidLayoutAlgorithm._internal(this._value);
@ -892,7 +885,7 @@ class AndroidLayoutAlgorithm {
int get hashCode => _value.hashCode;
}
///AndroidMixedContentMode class represents an Android-specific class used to configure the WebView's behavior when a secure origin attempts to load a resource from an insecure origin.
///Class that represents an Android-specific class used to configure the WebView's behavior when a secure origin attempts to load a resource from an insecure origin.
///
///**NOTE**: available on Android 21+.
class AndroidMixedContentMode {
@ -942,7 +935,7 @@ class AndroidMixedContentMode {
int get hashCode => _value.hashCode;
}
///IOSWKSelectionGranularity class represents an iOS-specific class used to set the level of granularity with which the user can interactively select content in the web view.
///Class that represents an iOS-specific class used to set the level of granularity with which the user can interactively select content in the web view.
class IOSWKSelectionGranularity {
final int _value;
const IOSWKSelectionGranularity._internal(this._value);
@ -977,7 +970,7 @@ class IOSWKSelectionGranularity {
int get hashCode => _value.hashCode;
}
///IOSWKDataDetectorTypes class represents an iOS-specific class used to specify a dataDetectoryTypes value that adds interactivity to web content that matches the value.
///Class that represents an iOS-specific class used to specify a dataDetectoryTypes value that adds interactivity to web content that matches the value.
///
///**NOTE**: available on iOS 10.0+.
class IOSWKDataDetectorTypes {
@ -1048,7 +1041,7 @@ class IOSWKDataDetectorTypes {
int get hashCode => _value.hashCode;
}
///IOSUIScrollViewDecelerationRate class represents a floating-point value that determines the rate of deceleration after the user lifts their finger.
///Class that represents a floating-point value that determines the rate of deceleration after the user lifts their finger.
class IOSUIScrollViewDecelerationRate {
final String _value;
const IOSUIScrollViewDecelerationRate._internal(this._value);
@ -1077,7 +1070,7 @@ class IOSUIScrollViewDecelerationRate {
int get hashCode => _value.hashCode;
}
///UserPreferredContentMode class represents the content mode to prefer when loading and rendering a webpage.
///Class that represents the content mode to prefer when loading and rendering a webpage.
class UserPreferredContentMode {
final int _value;
const UserPreferredContentMode._internal(this._value);
@ -1118,7 +1111,7 @@ class UserPreferredContentMode {
int get hashCode => _value.hashCode;
}
///IOSUIModalPresentationStyle class represents an iOS-specific class used to specify the modal presentation style when presenting a view controller.
///Class that represents an iOS-specific class used to specify the modal presentation style when presenting a view controller.
class IOSUIModalPresentationStyle {
final int _value;
const IOSUIModalPresentationStyle._internal(this._value);
@ -1201,7 +1194,7 @@ class IOSUIModalPresentationStyle {
int get hashCode => _value.hashCode;
}
///IOSUIModalTransitionStyle class represents an iOS-specific class used to specify the transition style when presenting a view controller.
///Class that represents an iOS-specific class used to specify the transition style when presenting a view controller.
class IOSUIModalTransitionStyle {
final int _value;
const IOSUIModalTransitionStyle._internal(this._value);
@ -1255,7 +1248,7 @@ class IOSUIModalTransitionStyle {
int get hashCode => _value.hashCode;
}
///IOSSafariDismissButtonStyle class represents an iOS-specific class used to set the custom style for the dismiss button.
///Class that represents an iOS-specific class used to set the custom style for the dismiss button.
///
///**NOTE**: available on iOS 11.0+.
class IOSSafariDismissButtonStyle {
@ -1296,8 +1289,8 @@ class IOSSafariDismissButtonStyle {
int get hashCode => _value.hashCode;
}
///InAppWebViewWidgetOptions class represents the options that can be used for an [InAppWebView].
class InAppWebViewWidgetOptions {
///Class that represents the options that can be used for a [WebView].
class InAppWebViewGroupOptions {
///Cross-platform options.
InAppWebViewOptions crossPlatform;
@ -1307,13 +1300,28 @@ class InAppWebViewWidgetOptions {
///iOS-specific options.
IOSInAppWebViewOptions ios;
InAppWebViewWidgetOptions(
InAppWebViewGroupOptions(
{this.crossPlatform,
this.android,
this.ios});
Map<String, dynamic> toMap() {
Map<String, dynamic> options = {};
options.addAll(this.crossPlatform?.toMap() ?? {});
if (Platform.isAndroid)
options.addAll(this.android?.toMap() ?? {});
else if (Platform.isIOS)
options.addAll(this.ios?.toMap() ?? {});
return options;
}
Map<String, dynamic> toJson() {
return this.toMap();
}
}
///InAppBrowserClassOptions class represents the options that can be used for an [InAppBrowser] WebView.
///Class that represents the options that can be used for an [InAppBrowser] WebView.
class InAppBrowserClassOptions {
///Cross-platform options.
InAppBrowserOptions crossPlatform;
@ -1325,16 +1333,42 @@ class InAppBrowserClassOptions {
IOSInAppBrowserOptions ios;
///WebView options.
InAppWebViewWidgetOptions inAppWebViewWidgetOptions;
InAppWebViewGroupOptions inAppWebViewGroupOptions;
InAppBrowserClassOptions(
{this.crossPlatform,
this.android,
this.ios,
this.inAppWebViewWidgetOptions});
this.inAppWebViewGroupOptions});
Map<String, dynamic> toMap() {
Map<String, dynamic> options = {};
options.addAll(this.crossPlatform?.toMap() ?? {});
options.addAll(
this.inAppWebViewGroupOptions?.crossPlatform?.toMap() ?? {});
if (Platform.isAndroid) {
options.addAll(this.android?.toMap() ?? {});
options.addAll(this
.inAppWebViewGroupOptions?.android
?.toMap() ??
{});
} else if (Platform.isIOS) {
options.addAll(this.ios?.toMap() ?? {});
options.addAll(
this.inAppWebViewGroupOptions?.ios?.toMap() ??
{});
}
return options;
}
Map<String, dynamic> toJson() {
return this.toMap();
}
}
///ChromeSafariBrowserClassOptions class represents the options that can be used for an [ChromeSafariBrowser] window.
///Class that represents the options that can be used for an [ChromeSafariBrowser] window.
class ChromeSafariBrowserClassOptions {
///Android-specific options.
AndroidChromeCustomTabsOptions android;
@ -1344,9 +1378,23 @@ class ChromeSafariBrowserClassOptions {
ChromeSafariBrowserClassOptions(
{this.android, this.ios});
Map<String, dynamic> toMap() {
Map<String, dynamic> options = {};
if (Platform.isAndroid)
options.addAll(this.android?.toMap() ?? {});
else if (Platform.isIOS)
options.addAll(this.ios?.toMap() ?? {});
return options;
}
Map<String, dynamic> toJson() {
return this.toMap();
}
}
///AjaxRequestAction class used by [AjaxRequest] class.
///Class used by [AjaxRequest] class.
class AjaxRequestAction {
final int _value;
const AjaxRequestAction._internal(this._value);
@ -1374,7 +1422,7 @@ class AjaxRequestAction {
}
}
///AjaxRequestEventType class used by [AjaxRequestEvent] class.
///Class used by [AjaxRequestEvent] class.
class AjaxRequestEventType {
final String _value;
const AjaxRequestEventType._internal(this._value);
@ -1416,7 +1464,7 @@ class AjaxRequestEventType {
int get hashCode => _value.hashCode;
}
///AjaxRequestEvent class used by [AjaxRequest] class. It represents events measuring progress of an [AjaxRequest].
///Class used by [AjaxRequest] class. It represents events measuring progress of an [AjaxRequest].
class AjaxRequestEvent {
///Event type.
AjaxRequestEventType type;
@ -1437,7 +1485,7 @@ class AjaxRequestEvent {
AjaxRequestEvent({this.type, this.lengthComputable, this.loaded, this.total});
}
///AjaxRequestReadyState class used by [AjaxRequest] class. It represents the state of an [AjaxRequest].
///Class used by [AjaxRequest] class. It represents the state of an [AjaxRequest].
class AjaxRequestReadyState {
final int _value;
const AjaxRequestReadyState._internal(this._value);
@ -1486,7 +1534,7 @@ class AjaxRequestReadyState {
int get hashCode => _value.hashCode;
}
///AjaxRequestHeaders class represents the HTTP headers of an [AjaxRequest].
///Class that represents the HTTP headers of an [AjaxRequest].
class AjaxRequestHeaders {
Map<dynamic, dynamic> _headers;
Map<String, dynamic> _newHeaders = {};
@ -1511,7 +1559,7 @@ class AjaxRequestHeaders {
}
}
///AjaxRequest class represents a JavaScript [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) object.
///Class that represents a JavaScript [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) object.
class AjaxRequest {
///Data passed as a parameter to the `XMLHttpRequest.send()` method.
dynamic data;
@ -1629,7 +1677,7 @@ class AjaxRequest {
}
}
///FetchRequestAction class used by [FetchRequest] class.
///Class used by [FetchRequest] class.
class FetchRequestAction {
final int _value;
const FetchRequestAction._internal(this._value);
@ -1647,7 +1695,7 @@ class FetchRequestAction {
int get hashCode => _value.hashCode;
}
///FetchRequestCredential class is an interface for [FetchRequestCredentialDefault], [FetchRequestFederatedCredential] and [FetchRequestPasswordCredential] classes.
///Class that is an interface for [FetchRequestCredentialDefault], [FetchRequestFederatedCredential] and [FetchRequestPasswordCredential] classes.
class FetchRequestCredential {
///Type of credentials.
String type;
@ -1659,7 +1707,7 @@ class FetchRequestCredential {
}
}
///FetchRequestCredentialDefault class represents the default credentials used by an [FetchRequest].
///Class that represents the default credentials used by an [FetchRequest].
class FetchRequestCredentialDefault extends FetchRequestCredential {
///The value of the credentials.
String value;
@ -1674,7 +1722,7 @@ class FetchRequestCredentialDefault extends FetchRequestCredential {
}
}
///FetchRequestFederatedCredential class represents a [FederatedCredential](https://developer.mozilla.org/en-US/docs/Web/API/FederatedCredential) type of credentials.
///Class that represents a [FederatedCredential](https://developer.mozilla.org/en-US/docs/Web/API/FederatedCredential) type of credentials.
class FetchRequestFederatedCredential extends FetchRequestCredential {
///Credential's identifier.
dynamic id;
@ -1707,7 +1755,7 @@ class FetchRequestFederatedCredential extends FetchRequestCredential {
}
}
///FetchRequestPasswordCredential class represents a [PasswordCredential](https://developer.mozilla.org/en-US/docs/Web/API/PasswordCredential) type of credentials.
///Class that represents a [PasswordCredential](https://developer.mozilla.org/en-US/docs/Web/API/PasswordCredential) type of credentials.
class FetchRequestPasswordCredential extends FetchRequestCredential {
///Credential's identifier.
dynamic id;
@ -1736,7 +1784,7 @@ class FetchRequestPasswordCredential extends FetchRequestCredential {
}
}
///FetchRequest class represents a HTTP request created with JavaScript using the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch).
///Class that represents a HTTP request created with JavaScript using the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch).
class FetchRequest {
///The URL of the request.
String url;
@ -1841,7 +1889,7 @@ class FetchRequest {
}
}
///ContentBlockerTriggerResourceType class represents the possible resource type defined for a [ContentBlockerTrigger].
///Class that represents the possible resource type defined for a [ContentBlockerTrigger].
class ContentBlockerTriggerResourceType {
final String _value;
const ContentBlockerTriggerResourceType._internal(this._value);
@ -1887,7 +1935,7 @@ class ContentBlockerTriggerResourceType {
int get hashCode => _value.hashCode;
}
///ContentBlockerTriggerLoadType class represents the possible load type for a [ContentBlockerTrigger].
///Class that represents the possible load type for a [ContentBlockerTrigger].
class ContentBlockerTriggerLoadType {
final String _value;
const ContentBlockerTriggerLoadType._internal(this._value);
@ -1915,7 +1963,7 @@ class ContentBlockerTriggerLoadType {
int get hashCode => _value.hashCode;
}
///ContentBlockerActionType class represents the kind of action that can be used with a [ContentBlockerTrigger].
///Class that represents the kind of action that can be used with a [ContentBlockerTrigger].
class ContentBlockerActionType {
final String _value;
const ContentBlockerActionType._internal(this._value);
@ -1948,7 +1996,7 @@ class ContentBlockerActionType {
int get hashCode => _value.hashCode;
}
///Cookie class represents a cookie returned by the [CookieManager].
///Class that represents a cookie returned by the [CookieManager].
class Cookie {
///The name;
String name;
@ -1959,7 +2007,7 @@ class Cookie {
Cookie({@required this.name, @required this.value});
}
///PermissionRequestResponseAction class used by [PermissionRequestResponse] class.
///Class used by [PermissionRequestResponse] class.
class PermissionRequestResponseAction {
final int _value;
const PermissionRequestResponseAction._internal(this._value);
@ -1977,7 +2025,7 @@ class PermissionRequestResponseAction {
int get hashCode => _value.hashCode;
}
///PermissionRequestResponse class represents the response used by the [androidOnPermissionRequest] event.
///Class that represents the response used by the [androidOnPermissionRequest] event.
class PermissionRequestResponse {
///Resources granted to be accessed by origin.
List<String> resources;
@ -1994,7 +2042,7 @@ class PermissionRequestResponse {
}
}
///ShouldOverrideUrlLoadingAction class is used by [shouldOverrideUrlLoading] event.
///Class that is used by [shouldOverrideUrlLoading] event.
///It represents the policy to pass back to the decision handler.
class ShouldOverrideUrlLoadingAction {
final int _value;
@ -2019,7 +2067,7 @@ class ShouldOverrideUrlLoadingAction {
}
}
///IOSWKNavigationType class represents the type of action triggering a navigation on iOS for the [shouldOverrideUrlLoading] event.
///Class that represents the type of action triggering a navigation on iOS for the [shouldOverrideUrlLoading] event.
class IOSWKNavigationType {
final int _value;
const IOSWKNavigationType._internal(this._value);
@ -2054,7 +2102,7 @@ class IOSWKNavigationType {
int get hashCode => _value.hashCode;
}
///ShouldOverrideUrlLoadingAction class represents the navigation request used by the [shouldOverrideUrlLoading] event.
///Class that represents the navigation request used by the [shouldOverrideUrlLoading] event.
class ShouldOverrideUrlLoadingRequest {
///Represents the url of the navigation request.
String url;
@ -2085,7 +2133,7 @@ class ShouldOverrideUrlLoadingRequest {
ShouldOverrideUrlLoadingRequest({this.url, this.method, this.headers, this.isForMainFrame, this.androidHasGesture, this.androidIsRedirect, this.iosWKNavigationType});
}
///OnCreateWindowRequest class represents the navigation request used by the [shouldOverrideUrlLoading] event.
///Class that represents the navigation request used by the [shouldOverrideUrlLoading] event.
class OnCreateWindowRequest {
///Represents the url of the navigation request.
String url;
@ -2102,7 +2150,7 @@ class OnCreateWindowRequest {
OnCreateWindowRequest({this.url, this.androidIsDialog, this.androidIsUserGesture, this.iosWKNavigationType});
}
///AndroidWebStorage class encapsulates information about the amount of storage currently used by an origin for the JavaScript storage APIs.
///Class that encapsulates information about the amount of storage currently used by an origin for the JavaScript storage APIs.
///An origin comprises the host, scheme and port of a URI. See [AndroidWebStorageManager] for details.
class AndroidWebStorageOrigin {
///The string representation of this origin.
@ -2129,7 +2177,7 @@ class AndroidWebStorageOrigin {
}
}
///IOSWKWebsiteDataType class represents a website data type.
///Class that represents a website data type.
///
///**NOTE**: available on iOS 9.0+.
class IOSWKWebsiteDataType {
@ -2220,7 +2268,7 @@ class IOSWKWebsiteDataType {
int get hashCode => _value.hashCode;
}
///WKWebsiteDataRecord class represents website data, grouped by domain name using the public suffix list.
///Class that represents website data, grouped by domain name using the public suffix list.
///
///**NOTE**: available on iOS 9.0+.
class IOSWKWebsiteDataRecord {
@ -2306,7 +2354,7 @@ class LongPressHitTestResultType {
int get hashCode => _value.hashCode;
}
///LongPressHitTestResult class represents the hit result for hitting an HTML elements. Used by [onLongPressHitTestResult] event.
///Class that represents the hit result for hitting an HTML elements. Used by [onLongPressHitTestResult] event.
class LongPressHitTestResult {
///The type of the hit test result.
LongPressHitTestResultType type;

View File

@ -5,7 +5,7 @@ import 'package:flutter/services.dart';
import 'types.dart';
///WebStorageManager class implements a singleton object (shared instance) which manages the web storage used by WebView instances.
///Class that implements a singleton object (shared instance) which manages the web storage used by WebView instances.
///
///**NOTE for iOS**: available from iOS 9.0+.
class WebStorageManager {
@ -30,7 +30,7 @@ class WebStorageManager {
static Future<dynamic> _handleMethod(MethodCall call) async {}
}
///AndroidWebStorageManager class is used to manage the JavaScript storage APIs provided by the WebView.
///Class that is used to manage the JavaScript storage APIs provided by the WebView.
///It manages the Application Cache API, the Web SQL Database API and the HTML5 Web Storage API.
class AndroidWebStorageManager {
///Gets the origins currently using either the Application Cache or Web SQL Database APIs.
@ -83,7 +83,7 @@ class AndroidWebStorageManager {
}
}
///IOSWebStorageManager class represents various types of data that a website might make use of.
///Class that represents various types of data that a website might make use of.
///This includes cookies, disk and memory caches, and persistent data such as WebSQL, IndexedDB databases, and local storage.
///
///**NOTE**: available on iOS 9.0+.

351
lib/src/webview.dart Normal file
View File

@ -0,0 +1,351 @@
import 'types.dart';
import 'in_app_webview_controller.dart';
///Abstract class that represents a WebView. Used by [WebView] and [HeadlessInAppWebView].
abstract class WebView {
///Event fired when the [WebView] is created.
final void Function(InAppWebViewController controller) onWebViewCreated;
///Event fired when the [WebView] starts to load an [url].
final void Function(InAppWebViewController controller, String url)
onLoadStart;
///Event fired when the [WebView] finishes loading an [url].
final void Function(InAppWebViewController controller, String url) onLoadStop;
///Event fired when the [WebView] encounters an error loading an [url].
final void Function(InAppWebViewController controller, String url, int code,
String message) onLoadError;
///Event fired when the [WebView] main page receives an HTTP error.
///
///[url] represents the url of the main page that received the HTTP error.
///
///[statusCode] represents the status code of the response. HTTP errors have status codes >= 400.
///
///[description] represents the description of the HTTP error. On iOS, it is always an empty string.
///
///**NOTE**: available on Android 23+.
final void Function(InAppWebViewController controller, String url,
int statusCode, String description) onLoadHttpError;
///Event fired when the current [progress] of loading a page is changed.
final void Function(InAppWebViewController controller, int progress)
onProgressChanged;
///Event fired when the [WebView] receives a [ConsoleMessage].
final void Function(
InAppWebViewController controller, ConsoleMessage consoleMessage)
onConsoleMessage;
///Give the host application a chance to take control when a URL is about to be loaded in the current WebView. This event is not called on the initial load of the WebView.
///
///Note that on Android there isn't any way to load an URL for a frame that is not the main frame, so if the request is not for the main frame, the navigation is allowed by default.
///However, if you want to cancel requests for subframes, you can use the [AndroidInAppWebViewOptions.regexToCancelSubFramesLoading] option
///to write a Regular Expression that, if the url request of a subframe matches, then the request of that subframe is canceled.
///
///Also, on Android, this method is not called for POST requests.
///
///[shouldOverrideUrlLoadingRequest] represents the navigation request.
///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldOverrideUrlLoading] option to `true`.
final Future<ShouldOverrideUrlLoadingAction> Function(
InAppWebViewController controller,
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest)
shouldOverrideUrlLoading;
///Event fired when the [WebView] loads a resource.
///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useOnLoadResource] and [InAppWebViewOptions.javaScriptEnabled] options to `true`.
final void Function(
InAppWebViewController controller, LoadedResource resource)
onLoadResource;
///Event fired when the [WebView] scrolls.
///
///[x] represents the current horizontal scroll origin in pixels.
///
///[y] represents the current vertical scroll origin in pixels.
final void Function(InAppWebViewController controller, int x, int y)
onScrollChanged;
///Event fired when [WebView] recognizes and starts a downloadable file.
///
///[url] represents the url of the file.
///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useOnDownloadStart] option to `true`.
final void Function(InAppWebViewController controller, String url)
onDownloadStart;
///Event fired when the [WebView] finds the `custom-scheme` while loading a resource. Here you can handle the url request and return a [CustomSchemeResponse] to load a specific resource encoded to `base64`.
///
///[scheme] represents the scheme of the url.
///
///[url] represents the url of the request.
final Future<CustomSchemeResponse> Function(
InAppWebViewController controller, String scheme, String url)
onLoadResourceCustomScheme;
///Event fired when the [WebView] requests the host application to create a new window,
///for example when trying to open a link with `target="_blank"` or when `window.open()` is called by JavaScript side.
///
///[onCreateWindowRequest] represents the request.
///
///**NOTE**: on Android you need to set [AndroidInAppWebViewOptions.supportMultipleWindows] option to `true`.
final void Function(InAppWebViewController controller,
OnCreateWindowRequest onCreateWindowRequest) onCreateWindow;
///Event fired when javascript calls the `alert()` method to display an alert dialog.
///If [JsAlertResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog.
///
///[message] represents the message to be displayed in the alert dialog.
final Future<JsAlertResponse> Function(
InAppWebViewController controller, String message) onJsAlert;
///Event fired when javascript calls the `confirm()` method to display a confirm dialog.
///If [JsConfirmResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog.
///
///[message] represents the message to be displayed in the alert dialog.
final Future<JsConfirmResponse> Function(
InAppWebViewController controller, String message) onJsConfirm;
///Event fired when javascript calls the `prompt()` method to display a prompt dialog.
///If [JsPromptResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog.
///
///[message] represents the message to be displayed in the alert dialog.
///
///[defaultValue] represents the default value displayed in the prompt dialog.
final Future<JsPromptResponse> Function(InAppWebViewController controller,
String message, String defaultValue) onJsPrompt;
///Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request.
///
///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [HttpAuthChallenge].
final Future<HttpAuthResponse> Function(
InAppWebViewController controller, HttpAuthChallenge challenge)
onReceivedHttpAuthRequest;
///Event fired when the WebView need to perform server trust authentication (certificate validation).
///The host application must return either [ServerTrustAuthResponse] instance with [ServerTrustAuthResponseAction.CANCEL] or [ServerTrustAuthResponseAction.PROCEED].
///
///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ServerTrustChallenge].
final Future<ServerTrustAuthResponse> Function(
InAppWebViewController controller, ServerTrustChallenge challenge)
onReceivedServerTrustAuthRequest;
///Notify the host application to handle an SSL client certificate request.
///Webview stores the response in memory (for the life of the application) if [ClientCertResponseAction.PROCEED] or [ClientCertResponseAction.CANCEL]
///is called and does not call [onReceivedClientCertRequest] again for the same host and port pair.
///Note that, multiple layers in chromium network stack might be caching the responses.
///
///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ClientCertChallenge].
final Future<ClientCertResponse> Function(
InAppWebViewController controller, ClientCertChallenge challenge)
onReceivedClientCertRequest;
///Event fired as find-on-page operations progress.
///The listener may be notified multiple times while the operation is underway, and the numberOfMatches value should not be considered final unless [isDoneCounting] is true.
///
///[activeMatchOrdinal] represents the zero-based ordinal of the currently selected match.
///
///[numberOfMatches] represents how many matches have been found.
///
///[isDoneCounting] whether the find operation has actually completed.
final void Function(InAppWebViewController controller, int activeMatchOrdinal,
int numberOfMatches, bool isDoneCounting) onFindResultReceived;
///Event fired when an `XMLHttpRequest` is sent to a server.
///It gives the host application a chance to take control over the request before sending it.
///
///[ajaxRequest] represents the `XMLHttpRequest`.
///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`.
///Also, unlike iOS that has [WKUserScript](https://developer.apple.com/documentation/webkit/wkuserscript) that
///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code
///used to intercept ajax requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms).
///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure.
final Future<AjaxRequest> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)
shouldInterceptAjaxRequest;
///Event fired whenever the `readyState` attribute of an `XMLHttpRequest` changes.
///It gives the host application a chance to abort the request.
///
///[ajaxRequest] represents the [XMLHttpRequest].
///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`.
///Also, unlike iOS that has [WKUserScript](https://developer.apple.com/documentation/webkit/wkuserscript) that
///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code
///used to intercept ajax requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms).
///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure.
final Future<AjaxRequestAction> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)
onAjaxReadyStateChange;
///Event fired as an `XMLHttpRequest` progress.
///It gives the host application a chance to abort the request.
///
///[ajaxRequest] represents the [XMLHttpRequest].
///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`.
///Also, unlike iOS that has [WKUserScript](https://developer.apple.com/documentation/webkit/wkuserscript) that
///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code
///used to intercept ajax requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms).
///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure.
final Future<AjaxRequestAction> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)
onAjaxProgress;
///Event fired when a request is sent to a server through [Fetch API](https://developer.mozilla.org/it/docs/Web/API/Fetch_API).
///It gives the host application a chance to take control over the request before sending it.
///
///[fetchRequest] represents a resource request.
///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptFetchRequest] option to `true`.
///Also, unlike iOS that has [WKUserScript](https://developer.apple.com/documentation/webkit/wkuserscript) that
///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code
///used to intercept fetch requests is loaded as soon as possible so it won't be instantaneous as iOS but just after some milliseconds (< ~100ms).
///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the fetch requests will be intercept for sure.
final Future<FetchRequest> Function(
InAppWebViewController controller, FetchRequest fetchRequest)
shouldInterceptFetchRequest;
///Event fired when the host application updates its visited links database.
///This event is also fired when the navigation state of the [WebView] changes through the usage of
///javascript **[History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API)** functions (`pushState()`, `replaceState()`) and `onpopstate` event
///or, also, when the javascript `window.location` changes without reloading the webview (for example appending or modifying an hash to the url).
///
///[url] represents the url being visited.
///
///[androidIsReload] indicates if this url is being reloaded. Available only on Android.
final void Function(
InAppWebViewController controller, String url, bool androidIsReload)
onUpdateVisitedHistory;
///Event fired when `window.print()` is called from JavaScript side.
///
///[url] represents the url on which is called.
///
///**NOTE**: available on Android 21+.
final void Function(InAppWebViewController controller, String url) onPrint;
///Event fired when an HTML element of the webview has been clicked and held.
///
///[hitTestResult] represents the hit result for hitting an HTML elements.
final void Function(InAppWebViewController controller,
LongPressHitTestResult hitTestResult) onLongPressHitTestResult;
///Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing.
///The default behavior is to show an interstitial to the user, with the reporting checkbox visible.
///
///[url] represents the url of the request.
///
///[threatType] represents the reason the resource was caught by Safe Browsing, corresponding to a [SafeBrowsingThreat].
///
///**NOTE**: available only on Android 27+.
final Future<SafeBrowsingResponse> Function(InAppWebViewController controller,
String url, SafeBrowsingThreat threatType) androidOnSafeBrowsingHit;
///Event fired when the WebView is requesting permission to access the specified resources and the permission currently isn't granted or denied.
///
///[origin] represents the origin of the web page which is trying to access the restricted resources.
///
///[resources] represents the array of resources the web content wants to access.
///
///**NOTE**: available only on Android 23+.
final Future<PermissionRequestResponse> Function(
InAppWebViewController controller,
String origin,
List<String> resources) androidOnPermissionRequest;
///Event that notifies the host application that web content from the specified origin is attempting to use the Geolocation API, but no permission state is currently set for that origin.
///Note that for applications targeting Android N and later SDKs (API level > `Build.VERSION_CODES.M`) this method is only called for requests originating from secure origins such as https.
///On non-secure origins geolocation requests are automatically denied.
///
///[origin] represents the origin of the web content attempting to use the Geolocation API.
///
///**NOTE**: available only on Android.
final Future<GeolocationPermissionShowPromptResponse> Function(
InAppWebViewController controller, String origin)
androidOnGeolocationPermissionsShowPrompt;
///Notify the host application that a request for Geolocation permissions, made with a previous call to [androidOnGeolocationPermissionsShowPrompt] has been canceled.
///Any related UI should therefore be hidden.
///
///**NOTE**: available only on Android.
final Future<void> Function(InAppWebViewController controller)
androidOnGeolocationPermissionsHidePrompt;
///Invoked when the web view's web content process is terminated.
///
///**NOTE**: available only on iOS.
final Future<void> Function(InAppWebViewController controller)
iosOnWebContentProcessDidTerminate;
///Called when the web view begins to receive web content.
///
///**NOTE**: available only on iOS.
final Future<void> Function(InAppWebViewController controller) iosOnDidCommit;
///Called when a web view receives a server redirect.
///
///**NOTE**: available only on iOS.
final Future<void> Function(InAppWebViewController controller)
iosOnDidReceiveServerRedirectForProvisionalNavigation;
///Initial url that will be loaded.
final String initialUrl;
///Initial asset file that will be loaded. See [InAppWebViewController.loadFile] for explanation.
final String initialFile;
///Initial [InAppWebViewInitialData] that will be loaded.
final InAppWebViewInitialData initialData;
///Initial headers that will be used.
final Map<String, String> initialHeaders;
///Initial options that will be used.
final InAppWebViewGroupOptions initialOptions;
WebView(
{this.onWebViewCreated,
this.onLoadStart,
this.onLoadStop,
this.onLoadError,
this.onLoadHttpError,
this.onProgressChanged,
this.onConsoleMessage,
this.shouldOverrideUrlLoading,
this.onLoadResource,
this.onScrollChanged,
this.onDownloadStart,
this.onLoadResourceCustomScheme,
this.onCreateWindow,
this.onJsAlert,
this.onJsConfirm,
this.onJsPrompt,
this.onReceivedHttpAuthRequest,
this.onReceivedServerTrustAuthRequest,
this.onReceivedClientCertRequest,
this.onFindResultReceived,
this.shouldInterceptAjaxRequest,
this.onAjaxReadyStateChange,
this.onAjaxProgress,
this.shouldInterceptFetchRequest,
this.onUpdateVisitedHistory,
this.onPrint,
this.onLongPressHitTestResult,
this.androidOnSafeBrowsingHit,
this.androidOnPermissionRequest,
this.androidOnGeolocationPermissionsShowPrompt,
this.androidOnGeolocationPermissionsHidePrompt,
this.iosOnWebContentProcessDidTerminate,
this.iosOnDidCommit,
this.iosOnDidReceiveServerRedirectForProvisionalNavigation,
this.initialUrl,
this.initialFile,
this.initialData,
this.initialHeaders,
this.initialOptions});
}

View File

@ -1,6 +1,6 @@
name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview or open an in-app browser window.
version: 3.0.0
version: 3.1.0
homepage: https://github.com/pichillilorenzo/flutter_inappwebview
environment:

View File

@ -4,6 +4,7 @@ export NODE_SERVER_IP=$1
dart tool/env.dart
cd nodejs_server_test_auth_basic_and_ssl
node index.js &
flutter clean
cd ../example
flutter clean
flutter driver -t test_driver/app.dart