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"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <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"> <codeStyleSettings language="XML">
<indentOptions> <indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" /> <option name="CONTINUATION_INDENT_SIZE" value="4" />

View File

@ -1,5 +1,5 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<state> <state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" /> <option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state> </state>
</component> </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"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="MarkdownProjectSettings" wasCopied="false"> <component name="FlexmarkProjectSettings">
<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"> <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> <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> </PanelProvider>
</PreviewSettings> </PreviewSettings>
<ParserSettings gitHubSyntaxChange="false" emojiShortcuts="1" emojiImages="0"> <ParserSettings gitHubSyntaxChange="false" correctedInvalidSettings="false" emojiShortcuts="1" emojiImages="0">
<PegdownExtensions> <PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" /> <option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" /> <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="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" /> <option name="INTELLIJ_DUMMY_IDENTIFIER" value="true" />
<option name="HARDWRAPS" value="false" />
<option name="HTML_DEEP_PARSER" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" /> <option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" /> <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="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" /> <option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="true" /> <option name="WIKILINKS" value="true" />
</PegdownExtensions> </PegdownExtensions>
<ParserOptions> <ParserOptions>
<option name="ADMONITION_EXT" value="false" />
<option name="ATTRIBUTES_EXT" value="false" />
<option name="COMMONMARK_LISTS" value="true" /> <option name="COMMONMARK_LISTS" value="true" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" /> <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="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="GITHUB_WIKI_LINKS" value="true" />
<option name="GITLAB_EXT" value="false" /> <option name="PRODUCTION_SPEC_PARSER" value="true" />
<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="SIM_TOC_BLANK_LINE_SPACER" value="true" /> <option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions> </ParserOptions>
</ParserSettings> </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> <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> </GeneratorProvider>
<headerTop /> <headerTop />
<headerBottom /> <headerBottom />
@ -68,15 +47,11 @@
</HtmlSettings> </HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true"> <CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider> <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> </StylesheetProvider>
<ScriptProviders /> <ScriptProviders />
<cssText /> <cssText />
<cssUriHistory /> <cssUriHistory />
</CssSettings> </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> </component>
</project> </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 ## 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` - 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: 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`. - [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. - [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. - [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`. - [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 { class MyApp extends StatefulWidget {
@override @override
_MyAppState createState() => new _MyAppState(); _MyAppState createState() => new _MyAppState();
} }z
class _MyAppState extends State<MyApp> { class _MyAppState extends State<MyApp> {
@ -177,7 +178,7 @@ class _MyAppState extends State<MyApp> {
child: InAppWebView( child: InAppWebView(
initialUrl: "https://flutter.dev/", initialUrl: "https://flutter.dev/",
initialHeaders: {}, initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
debuggingEnabled: true, 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`. * `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. * `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. * `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. * `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. * `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. * `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) `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]. 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. 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: So, on the JavaScript side, to get data coming from the Dart side, you will use:
```html ```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). * `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). * `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 ### `InAppBrowser` class
In-App Browser using native WebView. In-App Browser using native WebView.
@ -596,7 +718,7 @@ class _MyAppState extends State<MyApp> {
widget.browser.openFile( widget.browser.openFile(
assetFilePath: "assets/index.html", assetFilePath: "assets/index.html",
options: InAppBrowserClassOptions( options: InAppBrowserClassOptions(
inAppWebViewWidgetOptions: InAppWebViewWidgetOptions( inAppWebViewGroupOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: true, useShouldOverrideUrlLoading: true,
useOnLoadResource: true, useOnLoadResource: true,
@ -744,6 +866,16 @@ class _MyAppState extends State<MyApp> {
@override @override
void initState() { 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(); 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. * `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`. * `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 #### `ChromeSafariBrowser` options
@ -846,7 +981,7 @@ Future main() async {
child: InAppWebView( child: InAppWebView(
initialUrl: "http://localhost:8080/assets/index.html", initialUrl: "http://localhost:8080/assets/index.html",
initialHeaders: {}, initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions( initialOptions: InAppWebViewGroupOptions(
inAppWebViewOptions: InAppWebViewOptions( inAppWebViewOptions: InAppWebViewOptions(
debuggingEnabled: true, debuggingEnabled: true,
) )

View File

@ -2,8 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.pichillilorenzo.flutter_inappwebview"> package="com.pichillilorenzo.flutter_inappwebview">
<application> <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> <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> </application>
</manifest> </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; package com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs;
import android.app.Activity; import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent; import android.content.Intent;
import android.graphics.Color; import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import androidx.browser.customtabs.CustomTabsCallback; import androidx.browser.customtabs.CustomTabsCallback;
import androidx.browser.customtabs.CustomTabsIntent; import androidx.browser.customtabs.CustomTabsIntent;
import androidx.browser.customtabs.CustomTabsService; import androidx.browser.customtabs.CustomTabsService;
import androidx.browser.customtabs.CustomTabsSession; import androidx.browser.customtabs.CustomTabsSession;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
import com.pichillilorenzo.flutter_inappwebview.R; import com.pichillilorenzo.flutter_inappwebview.R;
import com.pichillilorenzo.flutter_inappwebview.Shared; import com.pichillilorenzo.flutter_inappwebview.Shared;
import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodCall;
@ -52,6 +55,8 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel.
options = new ChromeCustomTabsOptions(); options = new ChromeCustomTabsOptions();
options.parse((HashMap<String, Object>) b.getSerializable("options")); 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; final ChromeCustomTabsActivity chromeCustomTabsActivity = this;
customTabActivityHelper = new CustomTabActivityHelper(); customTabActivityHelper = new CustomTabActivityHelper();
@ -63,18 +68,17 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel.
customTabActivityHelper.mayLaunchUrl(uri, null, null); customTabActivityHelper.mayLaunchUrl(uri, null, null);
builder = new CustomTabsIntent.Builder(customTabsSession); builder = new CustomTabsIntent.Builder(customTabsSession);
prepareCustomTabs(menuItemList);
CustomTabsIntent customTabsIntent = builder.build(); CustomTabsIntent customTabsIntent = builder.build();
prepareCustomTabs(customTabsIntent); prepareCustomTabsIntent(customTabsIntent);
CustomTabActivityHelper.openCustomTab(chromeCustomTabsActivity, customTabsIntent, uri, CHROME_CUSTOM_TAB_REQUEST_CODE); CustomTabActivityHelper.openCustomTab(chromeCustomTabsActivity, customTabsIntent, uri, CHROME_CUSTOM_TAB_REQUEST_CODE);
} }
@Override @Override
public void onCustomTabsDisconnected() { public void onCustomTabsDisconnected() {
customTabsSession = null; chromeCustomTabsActivity.close();
finish();
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", uuid);
channel.invokeMethod("onChromeSafariBrowserClosed", obj);
} }
}); });
@ -122,12 +126,23 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel.
@Override @Override
public void onMethodCall(final MethodCall call, final MethodChannel.Result result) { public void onMethodCall(final MethodCall call, final MethodChannel.Result result) {
switch (call.method) { 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: default:
result.notImplemented(); result.notImplemented();
} }
} }
private void prepareCustomTabs(CustomTabsIntent customTabsIntent) { private void prepareCustomTabs(List<HashMap<String, Object>> menuItemList) {
if (options.addDefaultShareMenuItem) if (options.addDefaultShareMenuItem)
builder.addDefaultShareMenuItem(); builder.addDefaultShareMenuItem();
@ -141,6 +156,14 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel.
builder.setInstantAppsEnabled(options.instantAppsEnabled); 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) if (options.packageName != null)
customTabsIntent.intent.setPackage(options.packageName); customTabsIntent.intent.setPackage(options.packageName);
else else
@ -165,12 +188,27 @@ public class ChromeCustomTabsActivity extends Activity implements MethodChannel.
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CHROME_CUSTOM_TAB_REQUEST_CODE) { if (requestCode == CHROME_CUSTOM_TAB_REQUEST_CODE) {
customTabsSession = null; close();
finish();
Map<String, Object> obj = new HashMap<>();
obj.put("uuid", uuid);
InAppWebViewFlutterPlugin.inAppBrowserManager.channel.invokeMethod("onChromeSafariBrowserClosed", obj);
} }
} }
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.ChromeCustomTabsActivity;
import com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.CustomTabActivityHelper; 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.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.BinaryMessenger;
@ -40,7 +43,8 @@ public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandl
String uuidFallback = (String) call.argument("uuidFallback"); String uuidFallback = (String) call.argument("uuidFallback");
Map<String, String> headersFallback = (Map<String, String>) call.argument("headersFallback"); Map<String, String> headersFallback = (Map<String, String>) call.argument("headersFallback");
HashMap<String, Object> optionsFallback = (HashMap<String, Object>) call.argument("optionsFallback"); 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; break;
default: 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; Intent intent = null;
Bundle extras = new Bundle(); Bundle extras = new Bundle();
@ -58,6 +63,7 @@ public class ChromeSafariBrowserManager implements MethodChannel.MethodCallHandl
extras.putString("uuid", uuid); extras.putString("uuid", uuid);
extras.putSerializable("options", options); extras.putSerializable("options", options);
extras.putSerializable("headers", (Serializable) headersFallback); extras.putSerializable("headers", (Serializable) headersFallback);
extras.putSerializable("menuItemList", (Serializable) menuItemList);
if (CustomTabActivityHelper.isAvailable(activity)) { if (CustomTabActivityHelper.isAvailable(activity)) {
intent = new Intent(activity, ChromeCustomTabsActivity.class); 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.content.Intent;
import android.graphics.Bitmap; 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.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewOptions; 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.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
@ -65,6 +67,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
webView = findViewById(R.id.webView); webView = findViewById(R.id.webView);
webView.inAppBrowserActivity = this; webView.inAppBrowserActivity = this;
webView.channel = channel;
fromActivity = b.getString("fromActivity"); 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.HashMap;
import java.util.Map; import java.util.Map;

View File

@ -32,6 +32,8 @@ import android.os.Bundle;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import android.util.Log; import android.util.Log;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; 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.app.Activity;
import android.content.Context; 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.DisplayListenerProxy;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView; import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebViewOptions; 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.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@ -37,7 +39,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
public InAppWebView webView; public InAppWebView webView;
public final MethodChannel channel; 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 = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_" + id);
channel.setMethodCallHandler(this); channel.setMethodCallHandler(this);
@ -93,6 +95,12 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
} }
else else
webView.loadUrl(initialUrl, initialHeaders); 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 @Override

View File

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

View File

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

View File

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

View File

@ -7,6 +7,8 @@ import android.util.Log;
import android.webkit.JavascriptInterface; import android.webkit.JavascriptInterface;
import android.webkit.ValueCallback; 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 com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView;
import java.util.HashMap; import java.util.HashMap;

View File

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

View File

@ -3,7 +3,7 @@
xmlns:appcompat="http://schemas.android.com/apk/res-auto" xmlns:appcompat="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context=".InAppBrowserActivity"> tools:context=".InAppBrowser.InAppBrowserActivity">
<item <item
android:id="@+id/action_go_back" 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> { extends State<ChromeSafariBrowserExampleScreen> {
@override @override
void initState() { 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(); super.initState();
} }
@ -53,7 +63,10 @@ class _ChromeSafariBrowserExampleScreenState
url: "https://flutter.dev/", url: "https://flutter.dev/",
options: ChromeSafariBrowserClassOptions( options: ChromeSafariBrowserClassOptions(
android: AndroidChromeCustomTabsOptions(addDefaultShareMenuItem: false, keepAliveEnabled: true), 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")), 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( await widget.browser.openFile(
assetFilePath: "assets/index.html", assetFilePath: "assets/index.html",
options: InAppBrowserClassOptions( options: InAppBrowserClassOptions(
inAppWebViewWidgetOptions: InAppWebViewWidgetOptions( inAppWebViewGroupOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
debuggingEnabled: true, debuggingEnabled: true,
useShouldOverrideUrlLoading: true, useShouldOverrideUrlLoading: true,

View File

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

View File

@ -2,8 +2,8 @@ import 'dart:async';
import 'package:flutter/material.dart'; 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/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_webiew_example.screen.dart';
import 'package:flutter_inappwebview_example/in_app_browser_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, '/'); Navigator.pushReplacementNamed(context, '/');
}, },
), ),
ListTile(
title: Text('HeadlessInAppWebView'),
onTap: () {
Navigator.pushReplacementNamed(context, '/HeadlessInAppWebView');
},
),
], ],
), ),
); );
@ -74,6 +80,7 @@ class _MyAppState extends State<MyApp> {
'/': (context) => InAppWebViewExampleScreen(), '/': (context) => InAppWebViewExampleScreen(),
'/InAppBrowser': (context) => InAppBrowserExampleScreen(), '/InAppBrowser': (context) => InAppBrowserExampleScreen(),
'/ChromeSafariBrowser': (context) => ChromeSafariBrowserExampleScreen(), '/ChromeSafariBrowser': (context) => ChromeSafariBrowserExampleScreen(),
'/HeadlessInAppWebView': (context) => HeadlessInAppWebViewExampleScreen(),
} }
); );
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,7 +30,7 @@ class InAppWebViewSslRequestTestState extends WidgetTestState {
child: InAppWebView( child: InAppWebView(
initialUrl: "https://${environment["NODE_SERVER_IP"]}:4433/", initialUrl: "https://${environment["NODE_SERVER_IP"]}:4433/",
initialHeaders: {}, initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions( initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
clearCache: true, clearCache: true,
debuggingEnabled: 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/.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/.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/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$/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/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.pub" /> <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 uuidFallback: String = arguments!["uuidFallback"] as! String
let headersFallback = arguments!["headersFallback"] as! [String: String] let headersFallback = arguments!["headersFallback"] as! [String: String]
let optionsFallback = arguments!["optionsFallback"] as! [String: Any?] 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 break
default: default:
result(FlutterMethodNotImplemented) 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 let absoluteUrl = URL(string: url)!.absoluteURL
if self.previousStatusBarStyle == -1 { if self.previousStatusBarStyle == -1 {
@ -85,10 +86,12 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
} }
safari.uuid = uuid safari.uuid = uuid
safari.menuItemList = menuItemList
safari.prepareMethodChannel() safari.prepareMethodChannel()
safari.delegate = safari safari.delegate = safari
safari.tmpWindow = tmpWindow safari.tmpWindow = tmpWindow
safari.safariOptions = safariOptions safari.safariOptions = safariOptions
safari.prepareSafariBrowser()
tmpController.present(safari, animated: true) { tmpController.present(safari, animated: true) {
result(true) result(true)

View File

@ -12,22 +12,27 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
private weak var registrar: FlutterPluginRegistrar? private weak var registrar: FlutterPluginRegistrar?
var webView: InAppWebView? var webView: InAppWebView?
var viewId: Int64 = 0 var viewId: Any = 0
var channel: FlutterMethodChannel? var channel: FlutterMethodChannel?
var myView: UIView? 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() super.init()
self.registrar = registrar self.registrar = registrar
self.viewId = viewId self.viewId = viewId
myView = UIView(frame: frame) var channelName = ""
if let id = viewId as? Int64 {
let channelName = "com.pichillilorenzo/flutter_inappwebview_" + String(viewId) 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 = FlutterMethodChannel(name: channelName, binaryMessenger: registrar.messenger())
channel!.setMethodCallHandler(LeakAvoider(delegate: self).handle) channel!.setMethodCallHandler(LeakAvoider(delegate: self).handle)
myView = UIView(frame: frame)
let initialUrl = args["initialUrl"] as? String let initialUrl = args["initialUrl"] as? String
let initialFile = args["initialFile"] as? String let initialFile = args["initialFile"] as? String
let initialData = args["initialData"] as? [String: 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) 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 { 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 tmpWindow: UIWindow?
var safariOptions: SafariBrowserOptions? var safariOptions: SafariBrowserOptions?
var uuid: String = "" var uuid: String = ""
var menuItemList: [[String: Any]] = []
public static func register(with registrar: FlutterPluginRegistrar) { 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) { 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) { public override func viewWillAppear(_ animated: Bool) {
prepareSafariBrowser() // prepareSafariBrowser()
super.viewWillAppear(animated) super.viewWillAppear(animated)
onChromeSafariBrowserOpened() onChromeSafariBrowserOpened()
} }
@ -83,23 +93,25 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
} }
public func safariViewController(_ controller: SFSafariViewController, activityItemsFor URL: URL, title: String?) -> [UIActivity] { public func safariViewController(_ controller: SFSafariViewController, activityItemsFor URL: URL, title: String?) -> [UIActivity] {
// print("activityItemsFor") var uiActivities: [UIActivity] = []
// print(URL) menuItemList.forEach { (menuItem) in
// print(title) let activity = CustomUIActivity(uuid: uuid, id: menuItem["id"] as! Int64, url: URL, title: title, label: menuItem["label"] as? String, type: nil, image: nil)
return [] 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("excludedActivityTypesFor")
// print(URL) // print(URL)
// print(title) // print(title)
return [] // return []
} // }
//
public func safariViewController(_ controller: SFSafariViewController, initialLoadDidRedirectTo URL: URL) { // public func safariViewController(_ controller: SFSafariViewController, initialLoadDidRedirectTo URL: URL) {
// print("initialLoadDidRedirectTo") // print("initialLoadDidRedirectTo")
// print(URL) // print(URL)
} // }
public func onChromeSafariBrowserOpened() { public func onChromeSafariBrowserOpened() {
channel!.invokeMethod("onChromeSafariBrowserOpened", arguments: []) channel!.invokeMethod("onChromeSafariBrowserOpened", arguments: [])
@ -146,3 +158,54 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
return hexInt 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 myWebStorageManager: Any?
var credentialDatabase: CredentialDatabase? var credentialDatabase: CredentialDatabase?
var inAppBrowserManager: InAppBrowserManager? var inAppBrowserManager: InAppBrowserManager?
var headlessInAppWebViewManager: HeadlessInAppWebViewManager?
var chromeSafariBrowserManager: ChromeSafariBrowserManager? var chromeSafariBrowserManager: ChromeSafariBrowserManager?
var webViewControllers: [String: InAppBrowserWebViewController?] = [:] 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") registrar.register(FlutterWebViewFactory(registrar: registrar) as FlutterPlatformViewFactory, withId: "com.pichillilorenzo/flutter_inappwebview")
inAppBrowserManager = InAppBrowserManager(registrar: registrar) inAppBrowserManager = InAppBrowserManager(registrar: registrar)
headlessInAppWebViewManager = HeadlessInAppWebViewManager(registrar: registrar)
chromeSafariBrowserManager = ChromeSafariBrowserManager(registrar: registrar) chromeSafariBrowserManager = ChromeSafariBrowserManager(registrar: registrar)
inAppWebViewStatic = InAppWebViewStatic(registrar: registrar) inAppWebViewStatic = InAppWebViewStatic(registrar: registrar)
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {

View File

@ -22,6 +22,9 @@
library flutter_inappwebview; library flutter_inappwebview;
export 'src/types.dart'; 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_webview.dart';
export 'src/in_app_browser.dart'; export 'src/in_app_browser.dart';
export 'src/cookie_manager.dart'; export 'src/cookie_manager.dart';

View File

@ -1,5 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:collection';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -7,8 +7,6 @@ import 'package:flutter/services.dart';
import 'types.dart'; import 'types.dart';
import 'in_app_browser.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 ///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. ///and [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS.
/// ///
@ -16,6 +14,7 @@ import 'in_app_browser.dart';
class ChromeSafariBrowser { class ChromeSafariBrowser {
String uuid; String uuid;
InAppBrowser browserFallback; InAppBrowser browserFallback;
Map<int, ChromeSafariBrowserMenuItem> _menuItems = new HashMap();
bool _isOpened = false; bool _isOpened = false;
MethodChannel _channel; MethodChannel _channel;
static const MethodChannel _sharedChannel = const MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser'); static const MethodChannel _sharedChannel = const MethodChannel('com.pichillilorenzo/flutter_chromesafaribrowser');
@ -42,6 +41,12 @@ class ChromeSafariBrowser {
onClosed(); onClosed();
this._isOpened = false; this._isOpened = false;
break; 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: default:
throw UnimplementedError("Unimplemented ${call.method} method"); throw UnimplementedError("Unimplemented ${call.method} method");
} }
@ -64,49 +69,45 @@ class ChromeSafariBrowser {
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $url!'); this.throwIsAlreadyOpened(message: 'Cannot open $url!');
Map<String, dynamic> optionsMap = {}; List<Map<String, dynamic>> menuItemList = new List();
if (Platform.isAndroid) _menuItems.forEach((key, value) {
optionsMap.addAll(options.android?.toMap() ?? {}); menuItemList.add({
else if (Platform.isIOS) "id": value.id,
optionsMap.addAll(options.ios?.toMap() ?? {}); "label": value.label
});
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() ??
{});
}
}
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid); args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
args.putIfAbsent('options', () => optionsMap); args.putIfAbsent('options', () => options?.toMap() ?? {});
args.putIfAbsent('uuidFallback', args.putIfAbsent('uuidFallback',
() => (browserFallback != null) ? browserFallback.uuid : ''); () => (browserFallback != null) ? browserFallback.uuid : '');
args.putIfAbsent('headersFallback', () => headersFallback); args.putIfAbsent('headersFallback', () => headersFallback);
args.putIfAbsent('optionsFallback', () => optionsFallbackMap); args.putIfAbsent('optionsFallback', () => optionsFallback?.toMap() ?? {});
args.putIfAbsent('menuItemList', () => menuItemList);
await _sharedChannel.invokeMethod('open', args); await _sharedChannel.invokeMethod('open', args);
this._isOpened = true; 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. ///Event fires when the [ChromeSafariBrowser] is opened.
void onOpened() {} 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 'package:flutter/foundation.dart';
import 'types.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 iOS, it uses [WKContentRuleListStore](https://developer.apple.com/documentation/webkit/wkcontentruleliststore).
///On Android, it uses a custom implementation because such functionality doesn't exist. ///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'; 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+. ///**NOTE for iOS**: available from iOS 11.0+.
class CookieManager { 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 'types.dart';
import 'package:flutter/services.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 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) ///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`. ///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/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'in_app_webview_controller.dart';
import 'webview_options.dart'; import 'webview_options.dart';
import 'types.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. ///This class uses the native WebView of the platform.
///The [webViewController] field can be used to access the [InAppWebViewController] API.
class InAppBrowser { class InAppBrowser {
String uuid; String uuid;
Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap = Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap =
@ -20,7 +19,7 @@ class InAppBrowser {
MethodChannel _channel; MethodChannel _channel;
static const MethodChannel _sharedChannel = const MethodChannel('com.pichillilorenzo/flutter_inappbrowser'); 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; InAppWebViewController webViewController;
/// ///
@ -63,29 +62,11 @@ class InAppBrowser {
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $url!'); 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>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid); args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
args.putIfAbsent('headers', () => headers); args.putIfAbsent('headers', () => headers);
args.putIfAbsent('options', () => optionsMap); args.putIfAbsent('options', () => options?.toMap() ?? {});
await _sharedChannel.invokeMethod('openUrl', args); await _sharedChannel.invokeMethod('openUrl', args);
} }
@ -129,29 +110,13 @@ class InAppBrowser {
assert(assetFilePath != null && assetFilePath.isNotEmpty); assert(assetFilePath != null && assetFilePath.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $assetFilePath!'); 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>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid); args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('url', () => assetFilePath); args.putIfAbsent('url', () => assetFilePath);
args.putIfAbsent('headers', () => headers); args.putIfAbsent('headers', () => headers);
args.putIfAbsent('options', () => optionsMap); args.putIfAbsent('options', () => options?.toMap() ?? {});
await _sharedChannel.invokeMethod('openFile', args); await _sharedChannel.invokeMethod('openFile', args);
} }
@ -173,27 +138,9 @@ class InAppBrowser {
InAppBrowserClassOptions options}) async { InAppBrowserClassOptions options}) async {
assert(data != null); 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>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid); args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('options', () => optionsMap); args.putIfAbsent('options', () => options?.toMap() ?? {});
args.putIfAbsent('data', () => data); args.putIfAbsent('data', () => data);
args.putIfAbsent('mimeType', () => mimeType); args.putIfAbsent('mimeType', () => mimeType);
args.putIfAbsent('encoding', () => encoding); args.putIfAbsent('encoding', () => encoding);
@ -243,26 +190,8 @@ class InAppBrowser {
Future<void> setOptions({@required InAppBrowserClassOptions options}) async { Future<void> setOptions({@required InAppBrowserClassOptions options}) async {
this.throwIsNotOpened(); 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>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('options', () => optionsMap); args.putIfAbsent('options', () => options?.toMap() ?? {});
await _channel.invokeMethod('setOptions', args); await _channel.invokeMethod('setOptions', args);
} }
@ -279,19 +208,19 @@ class InAppBrowser {
options = options.cast<String, dynamic>(); options = options.cast<String, dynamic>();
inAppBrowserClassOptions.crossPlatform = inAppBrowserClassOptions.crossPlatform =
InAppBrowserOptions.fromMap(options); InAppBrowserOptions.fromMap(options);
inAppBrowserClassOptions.inAppWebViewWidgetOptions = InAppWebViewWidgetOptions(); inAppBrowserClassOptions.inAppWebViewGroupOptions = InAppWebViewGroupOptions();
inAppBrowserClassOptions.inAppWebViewWidgetOptions.crossPlatform = inAppBrowserClassOptions.inAppWebViewGroupOptions.crossPlatform =
InAppWebViewOptions.fromMap(options); InAppWebViewOptions.fromMap(options);
if (Platform.isAndroid) { if (Platform.isAndroid) {
inAppBrowserClassOptions.android = inAppBrowserClassOptions.android =
AndroidInAppBrowserOptions.fromMap(options); AndroidInAppBrowserOptions.fromMap(options);
inAppBrowserClassOptions inAppBrowserClassOptions
.inAppWebViewWidgetOptions.android = .inAppWebViewGroupOptions.android =
AndroidInAppWebViewOptions.fromMap(options); AndroidInAppWebViewOptions.fromMap(options);
} else if (Platform.isIOS) { } else if (Platform.isIOS) {
inAppBrowserClassOptions.ios = inAppBrowserClassOptions.ios =
IOSInAppBrowserOptions.fromMap(options); IOSInAppBrowserOptions.fromMap(options);
inAppBrowserClassOptions.inAppWebViewWidgetOptions inAppBrowserClassOptions.inAppWebViewGroupOptions
.ios = IOSInAppWebViewOptions.fromMap(options); .ios = IOSInAppWebViewOptions.fromMap(options);
} }
} }

View File

@ -4,8 +4,6 @@ import 'dart:async';
import 'package:flutter/services.dart' show rootBundle; import 'package:flutter/services.dart' show rootBundle;
import 'package:mime/mime.dart'; 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`. ///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 { class InAppLocalhostServer {
HttpServer _server; 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 'dart:typed_data';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'webview_options.dart'; import 'webview_options.dart';
@ -183,8 +182,6 @@ class ConsoleMessage {
{this.message = "", this.messageLevel = ConsoleMessageLevel.LOG}); {this.message = "", this.messageLevel = ConsoleMessageLevel.LOG});
} }
///WebHistory class.
///
///This class contains a snapshot of the current back/forward list for a WebView. ///This class contains a snapshot of the current back/forward list for a WebView.
class WebHistory { class WebHistory {
///List of all [WebHistoryItem]s. ///List of all [WebHistoryItem]s.
@ -196,8 +193,6 @@ class WebHistory {
WebHistory({this.list, this.currentIndex}); 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. ///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 { class WebHistoryItem {
///Original url of this history item. ///Original url of this history item.
@ -219,8 +214,6 @@ class WebHistoryItem {
{this.originalUrl, this.title, this.url, this.index, this.offset}); {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 used by the host application to set the Geolocation permission state for an origin during the [androidOnGeolocationPermissionsShowPrompt] event.
class GeolocationPermissionShowPromptResponse { class GeolocationPermissionShowPromptResponse {
///The origin for which permissions are set. ///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 { class JsAlertResponseAction {
final int _value; final int _value;
const JsAlertResponseAction._internal(this._value); const JsAlertResponseAction._internal(this._value);
@ -254,7 +247,7 @@ class JsAlertResponseAction {
int get hashCode => _value.hashCode; 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 { class JsAlertResponse {
///Message to be displayed in the window. ///Message to be displayed in the window.
String message; String message;
@ -284,7 +277,7 @@ class JsAlertResponse {
} }
} }
///JsConfirmResponseAction class used by [JsConfirmResponse] class. ///Class used by [JsConfirmResponse] class.
class JsConfirmResponseAction { class JsConfirmResponseAction {
final int _value; final int _value;
const JsConfirmResponseAction._internal(this._value); const JsConfirmResponseAction._internal(this._value);
@ -299,7 +292,7 @@ class JsConfirmResponseAction {
int get hashCode => _value.hashCode; 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 { class JsConfirmResponse {
///Message to be displayed in the window. ///Message to be displayed in the window.
String message; String message;
@ -334,7 +327,7 @@ class JsConfirmResponse {
} }
} }
///JsPromptResponseAction class used by [JsPromptResponse] class. ///Class used by [JsPromptResponse] class.
class JsPromptResponseAction { class JsPromptResponseAction {
final int _value; final int _value;
const JsPromptResponseAction._internal(this._value); const JsPromptResponseAction._internal(this._value);
@ -349,7 +342,7 @@ class JsPromptResponseAction {
int get hashCode => _value.hashCode; 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 { class JsPromptResponse {
///Message to be displayed in the window. ///Message to be displayed in the window.
String message; 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 { class SafeBrowsingThreat {
final int _value; final int _value;
const SafeBrowsingThreat._internal(this._value); const SafeBrowsingThreat._internal(this._value);
@ -438,7 +431,7 @@ class SafeBrowsingThreat {
int get hashCode => _value.hashCode; int get hashCode => _value.hashCode;
} }
///SafeBrowsingResponseAction class used by [SafeBrowsingResponse] class. ///Class used by [SafeBrowsingResponse] class.
class SafeBrowsingResponseAction { class SafeBrowsingResponseAction {
final int _value; final int _value;
const SafeBrowsingResponseAction._internal(this._value); const SafeBrowsingResponseAction._internal(this._value);
@ -460,7 +453,7 @@ class SafeBrowsingResponseAction {
int get hashCode => _value.hashCode; 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. ///It is used to indicate an action to take when hitting a malicious URL.
class SafeBrowsingResponse { class SafeBrowsingResponse {
///If reporting is enabled, all reports will be sent according to the privacy policy referenced by [InAppWebViewController.androidGetSafeBrowsingPrivacyPolicyUrl]. ///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 { class HttpAuthResponseAction {
final int _value; final int _value;
const HttpAuthResponseAction._internal(this._value); const HttpAuthResponseAction._internal(this._value);
@ -500,7 +493,7 @@ class HttpAuthResponseAction {
int get hashCode => _value.hashCode; 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 { class HttpAuthResponse {
///Represents the username used for the authentication if the [action] corresponds to [HttpAuthResponseAction.PROCEED] ///Represents the username used for the authentication if the [action] corresponds to [HttpAuthResponseAction.PROCEED]
String username; 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. ///It provides all the information about the challenge.
class HttpAuthChallenge { class HttpAuthChallenge {
///A count of previous failed authentication attempts. ///A count of previous failed authentication attempts.
@ -544,7 +537,7 @@ class HttpAuthChallenge {
: assert(previousFailureCount != null && protectionSpace != null); : assert(previousFailureCount != null && protectionSpace != null);
} }
///ProtectionSpace class represents a protection space requiring authentication. ///Class that represents a protection space requiring authentication.
class ProtectionSpace { class ProtectionSpace {
///The hostname of the server. ///The hostname of the server.
String host; String host;
@ -565,7 +558,7 @@ class ProtectionSpace {
: assert(host != null && protocol != null); : 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. ///It is used by the [HttpAuthCredentialDatabase] class.
class HttpAuthCredential { class HttpAuthCredential {
///Represents the username. ///Represents the username.
@ -578,7 +571,7 @@ class HttpAuthCredential {
: assert(username != null && password != null); : assert(username != null && password != null);
} }
///ServerTrustAuthResponseAction class used by [ServerTrustAuthResponse] class. ///Class used by [ServerTrustAuthResponse] class.
class ServerTrustAuthResponseAction { class ServerTrustAuthResponseAction {
final int _value; final int _value;
const ServerTrustAuthResponseAction._internal(this._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. ///It provides all the information about the challenge.
class ServerTrustChallenge { class ServerTrustChallenge {
///The protection space requiring authentication. ///The protection space requiring authentication.
@ -635,7 +628,7 @@ class ServerTrustChallenge {
: assert(protectionSpace != null && error != null); : assert(protectionSpace != null && error != null);
} }
///ClientCertResponseAction class used by [ClientCertResponse] class. ///Class used by [ClientCertResponse] class.
class ClientCertResponseAction { class ClientCertResponseAction {
final int _value; final int _value;
const ClientCertResponseAction._internal(this._value); const ClientCertResponseAction._internal(this._value);
@ -656,7 +649,7 @@ class ClientCertResponseAction {
int get hashCode => _value.hashCode; 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 { class ClientCertResponse {
///The file path of the certificate to use. ///The file path of the certificate to use.
String certificatePath; 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. ///It provides all the information about the challenge.
class ClientCertChallenge { class ClientCertChallenge {
///The protection space requiring authentication. ///The protection space requiring authentication.
@ -699,7 +692,7 @@ class ClientCertChallenge {
: assert(protectionSpace != null); : 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 { class Favicon {
///The url of the favicon image. ///The url of the favicon image.
String url; 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 { class AndroidCacheMode {
final int _value; final int _value;
const AndroidCacheMode._internal(this._value); const AndroidCacheMode._internal(this._value);
@ -768,7 +761,7 @@ class AndroidCacheMode {
int get hashCode => _value.hashCode; 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+. ///**NOTE**: available on Android 24+.
class AndroidActionModeMenuItem { class AndroidActionModeMenuItem {
@ -818,7 +811,7 @@ class AndroidActionModeMenuItem {
int get hashCode => _value.hashCode; 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+. ///**NOTE**: available on Android 29+.
class AndroidForceDark { class AndroidForceDark {
@ -861,7 +854,7 @@ class AndroidForceDark {
int get hashCode => _value.hashCode; 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 { class AndroidLayoutAlgorithm {
final String _value; final String _value;
const AndroidLayoutAlgorithm._internal(this._value); const AndroidLayoutAlgorithm._internal(this._value);
@ -892,7 +885,7 @@ class AndroidLayoutAlgorithm {
int get hashCode => _value.hashCode; 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+. ///**NOTE**: available on Android 21+.
class AndroidMixedContentMode { class AndroidMixedContentMode {
@ -942,7 +935,7 @@ class AndroidMixedContentMode {
int get hashCode => _value.hashCode; 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 { class IOSWKSelectionGranularity {
final int _value; final int _value;
const IOSWKSelectionGranularity._internal(this._value); const IOSWKSelectionGranularity._internal(this._value);
@ -977,7 +970,7 @@ class IOSWKSelectionGranularity {
int get hashCode => _value.hashCode; 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+. ///**NOTE**: available on iOS 10.0+.
class IOSWKDataDetectorTypes { class IOSWKDataDetectorTypes {
@ -1048,7 +1041,7 @@ class IOSWKDataDetectorTypes {
int get hashCode => _value.hashCode; 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 { class IOSUIScrollViewDecelerationRate {
final String _value; final String _value;
const IOSUIScrollViewDecelerationRate._internal(this._value); const IOSUIScrollViewDecelerationRate._internal(this._value);
@ -1077,7 +1070,7 @@ class IOSUIScrollViewDecelerationRate {
int get hashCode => _value.hashCode; 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 { class UserPreferredContentMode {
final int _value; final int _value;
const UserPreferredContentMode._internal(this._value); const UserPreferredContentMode._internal(this._value);
@ -1118,7 +1111,7 @@ class UserPreferredContentMode {
int get hashCode => _value.hashCode; 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 { class IOSUIModalPresentationStyle {
final int _value; final int _value;
const IOSUIModalPresentationStyle._internal(this._value); const IOSUIModalPresentationStyle._internal(this._value);
@ -1201,7 +1194,7 @@ class IOSUIModalPresentationStyle {
int get hashCode => _value.hashCode; 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 { class IOSUIModalTransitionStyle {
final int _value; final int _value;
const IOSUIModalTransitionStyle._internal(this._value); const IOSUIModalTransitionStyle._internal(this._value);
@ -1255,7 +1248,7 @@ class IOSUIModalTransitionStyle {
int get hashCode => _value.hashCode; 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+. ///**NOTE**: available on iOS 11.0+.
class IOSSafariDismissButtonStyle { class IOSSafariDismissButtonStyle {
@ -1296,8 +1289,8 @@ class IOSSafariDismissButtonStyle {
int get hashCode => _value.hashCode; int get hashCode => _value.hashCode;
} }
///InAppWebViewWidgetOptions class represents the options that can be used for an [InAppWebView]. ///Class that represents the options that can be used for a [WebView].
class InAppWebViewWidgetOptions { class InAppWebViewGroupOptions {
///Cross-platform options. ///Cross-platform options.
InAppWebViewOptions crossPlatform; InAppWebViewOptions crossPlatform;
@ -1307,13 +1300,28 @@ class InAppWebViewWidgetOptions {
///iOS-specific options. ///iOS-specific options.
IOSInAppWebViewOptions ios; IOSInAppWebViewOptions ios;
InAppWebViewWidgetOptions( InAppWebViewGroupOptions(
{this.crossPlatform, {this.crossPlatform,
this.android, this.android,
this.ios}); 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 { class InAppBrowserClassOptions {
///Cross-platform options. ///Cross-platform options.
InAppBrowserOptions crossPlatform; InAppBrowserOptions crossPlatform;
@ -1325,16 +1333,42 @@ class InAppBrowserClassOptions {
IOSInAppBrowserOptions ios; IOSInAppBrowserOptions ios;
///WebView options. ///WebView options.
InAppWebViewWidgetOptions inAppWebViewWidgetOptions; InAppWebViewGroupOptions inAppWebViewGroupOptions;
InAppBrowserClassOptions( InAppBrowserClassOptions(
{this.crossPlatform, {this.crossPlatform,
this.android, this.android,
this.ios, 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 { class ChromeSafariBrowserClassOptions {
///Android-specific options. ///Android-specific options.
AndroidChromeCustomTabsOptions android; AndroidChromeCustomTabsOptions android;
@ -1344,9 +1378,23 @@ class ChromeSafariBrowserClassOptions {
ChromeSafariBrowserClassOptions( ChromeSafariBrowserClassOptions(
{this.android, this.ios}); {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 { class AjaxRequestAction {
final int _value; final int _value;
const AjaxRequestAction._internal(this._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 { class AjaxRequestEventType {
final String _value; final String _value;
const AjaxRequestEventType._internal(this._value); const AjaxRequestEventType._internal(this._value);
@ -1416,7 +1464,7 @@ class AjaxRequestEventType {
int get hashCode => _value.hashCode; 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 { class AjaxRequestEvent {
///Event type. ///Event type.
AjaxRequestEventType type; AjaxRequestEventType type;
@ -1437,7 +1485,7 @@ class AjaxRequestEvent {
AjaxRequestEvent({this.type, this.lengthComputable, this.loaded, this.total}); 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 { class AjaxRequestReadyState {
final int _value; final int _value;
const AjaxRequestReadyState._internal(this._value); const AjaxRequestReadyState._internal(this._value);
@ -1486,7 +1534,7 @@ class AjaxRequestReadyState {
int get hashCode => _value.hashCode; 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 { class AjaxRequestHeaders {
Map<dynamic, dynamic> _headers; Map<dynamic, dynamic> _headers;
Map<String, dynamic> _newHeaders = {}; 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 { class AjaxRequest {
///Data passed as a parameter to the `XMLHttpRequest.send()` method. ///Data passed as a parameter to the `XMLHttpRequest.send()` method.
dynamic data; dynamic data;
@ -1629,7 +1677,7 @@ class AjaxRequest {
} }
} }
///FetchRequestAction class used by [FetchRequest] class. ///Class used by [FetchRequest] class.
class FetchRequestAction { class FetchRequestAction {
final int _value; final int _value;
const FetchRequestAction._internal(this._value); const FetchRequestAction._internal(this._value);
@ -1647,7 +1695,7 @@ class FetchRequestAction {
int get hashCode => _value.hashCode; 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 { class FetchRequestCredential {
///Type of credentials. ///Type of credentials.
String type; 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 { class FetchRequestCredentialDefault extends FetchRequestCredential {
///The value of the credentials. ///The value of the credentials.
String value; 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 { class FetchRequestFederatedCredential extends FetchRequestCredential {
///Credential's identifier. ///Credential's identifier.
dynamic id; 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 { class FetchRequestPasswordCredential extends FetchRequestCredential {
///Credential's identifier. ///Credential's identifier.
dynamic id; 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 { class FetchRequest {
///The URL of the request. ///The URL of the request.
String url; 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 { class ContentBlockerTriggerResourceType {
final String _value; final String _value;
const ContentBlockerTriggerResourceType._internal(this._value); const ContentBlockerTriggerResourceType._internal(this._value);
@ -1887,7 +1935,7 @@ class ContentBlockerTriggerResourceType {
int get hashCode => _value.hashCode; 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 { class ContentBlockerTriggerLoadType {
final String _value; final String _value;
const ContentBlockerTriggerLoadType._internal(this._value); const ContentBlockerTriggerLoadType._internal(this._value);
@ -1915,7 +1963,7 @@ class ContentBlockerTriggerLoadType {
int get hashCode => _value.hashCode; 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 { class ContentBlockerActionType {
final String _value; final String _value;
const ContentBlockerActionType._internal(this._value); const ContentBlockerActionType._internal(this._value);
@ -1948,7 +1996,7 @@ class ContentBlockerActionType {
int get hashCode => _value.hashCode; 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 { class Cookie {
///The name; ///The name;
String name; String name;
@ -1959,7 +2007,7 @@ class Cookie {
Cookie({@required this.name, @required this.value}); Cookie({@required this.name, @required this.value});
} }
///PermissionRequestResponseAction class used by [PermissionRequestResponse] class. ///Class used by [PermissionRequestResponse] class.
class PermissionRequestResponseAction { class PermissionRequestResponseAction {
final int _value; final int _value;
const PermissionRequestResponseAction._internal(this._value); const PermissionRequestResponseAction._internal(this._value);
@ -1977,7 +2025,7 @@ class PermissionRequestResponseAction {
int get hashCode => _value.hashCode; 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 { class PermissionRequestResponse {
///Resources granted to be accessed by origin. ///Resources granted to be accessed by origin.
List<String> resources; 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. ///It represents the policy to pass back to the decision handler.
class ShouldOverrideUrlLoadingAction { class ShouldOverrideUrlLoadingAction {
final int _value; 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 { class IOSWKNavigationType {
final int _value; final int _value;
const IOSWKNavigationType._internal(this._value); const IOSWKNavigationType._internal(this._value);
@ -2054,7 +2102,7 @@ class IOSWKNavigationType {
int get hashCode => _value.hashCode; 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 { class ShouldOverrideUrlLoadingRequest {
///Represents the url of the navigation request. ///Represents the url of the navigation request.
String url; String url;
@ -2085,7 +2133,7 @@ class ShouldOverrideUrlLoadingRequest {
ShouldOverrideUrlLoadingRequest({this.url, this.method, this.headers, this.isForMainFrame, this.androidHasGesture, this.androidIsRedirect, this.iosWKNavigationType}); 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 { class OnCreateWindowRequest {
///Represents the url of the navigation request. ///Represents the url of the navigation request.
String url; String url;
@ -2102,7 +2150,7 @@ class OnCreateWindowRequest {
OnCreateWindowRequest({this.url, this.androidIsDialog, this.androidIsUserGesture, this.iosWKNavigationType}); 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. ///An origin comprises the host, scheme and port of a URI. See [AndroidWebStorageManager] for details.
class AndroidWebStorageOrigin { class AndroidWebStorageOrigin {
///The string representation of this origin. ///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+. ///**NOTE**: available on iOS 9.0+.
class IOSWKWebsiteDataType { class IOSWKWebsiteDataType {
@ -2220,7 +2268,7 @@ class IOSWKWebsiteDataType {
int get hashCode => _value.hashCode; 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+. ///**NOTE**: available on iOS 9.0+.
class IOSWKWebsiteDataRecord { class IOSWKWebsiteDataRecord {
@ -2306,7 +2354,7 @@ class LongPressHitTestResultType {
int get hashCode => _value.hashCode; 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 { class LongPressHitTestResult {
///The type of the hit test result. ///The type of the hit test result.
LongPressHitTestResultType type; LongPressHitTestResultType type;

View File

@ -5,7 +5,7 @@ import 'package:flutter/services.dart';
import 'types.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+. ///**NOTE for iOS**: available from iOS 9.0+.
class WebStorageManager { class WebStorageManager {
@ -30,7 +30,7 @@ class WebStorageManager {
static Future<dynamic> _handleMethod(MethodCall call) async {} 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. ///It manages the Application Cache API, the Web SQL Database API and the HTML5 Web Storage API.
class AndroidWebStorageManager { class AndroidWebStorageManager {
///Gets the origins currently using either the Application Cache or Web SQL Database APIs. ///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. ///This includes cookies, disk and memory caches, and persistent data such as WebSQL, IndexedDB databases, and local storage.
/// ///
///**NOTE**: available on iOS 9.0+. ///**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 name: flutter_inappwebview
description: A Flutter plugin that allows you to add an inline webview or open an in-app browser window. 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 homepage: https://github.com/pichillilorenzo/flutter_inappwebview
environment: environment:

View File

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