Fixed error 'java.lang.ClassCastException: cannot be cast to android.view.WindowManagerImpl' on Android when using native alert dialogs, updated README.md with Xcode version required

This commit is contained in:
Lorenzo Pichilli 2019-12-01 12:55:06 +01:00
parent a56b21282f
commit ade4480c6d
19 changed files with 1623 additions and 756 deletions

View File

@ -15,7 +15,25 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="9b41f7a2-a71e-4923-91fb-249d7815b3e7" name="Default" comment=""> <list default="true" id="9b41f7a2-a71e-4923-91fb-249d7815b3e7" name="Default" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/CHANGELOG.md" beforeDir="false" afterPath="$PROJECT_DIR$/CHANGELOG.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" /> <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewChromeClient.java" beforeDir="false" afterPath="$PROJECT_DIR$/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewChromeClient.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/ios/Flutter/flutter_export_environment.sh" beforeDir="false" afterPath="$PROJECT_DIR$/example/ios/Flutter/flutter_export_environment.sh" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/ios/Runner.xcodeproj/project.pbxproj" beforeDir="false" afterPath="$PROJECT_DIR$/example/ios/Runner.xcodeproj/project.pbxproj" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/pubspec.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/example/pubspec.yaml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/example/test_driver/in_app_webview_on_load_error_test.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/test_driver/in_app_webview_on_load_error_test.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/channel_manager.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/channel_manager.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/chrome_safari_browser.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/chrome_safari_browser.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/content_blocker.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/content_blocker.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/cookie_manager.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/cookie_manager.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/in_app_browser.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/in_app_browser.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/in_app_localhost_server.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/in_app_localhost_server.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/in_app_webview.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/in_app_webview.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/types.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/types.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/lib/src/webview_options.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/src/webview_options.dart" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pubspec.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/pubspec.yaml" afterDir="false" />
</list> </list>
<ignored path="$PROJECT_DIR$/.dart_tool/" /> <ignored path="$PROJECT_DIR$/.dart_tool/" />
<ignored path="$PROJECT_DIR$/.idea/" /> <ignored path="$PROJECT_DIR$/.idea/" />
@ -35,12 +53,13 @@
<component name="ExecutionTargetManager" SELECTED_TARGET="Pixel_3_XL_API_24" /> <component name="ExecutionTargetManager" SELECTED_TARGET="Pixel_3_XL_API_24" />
<component name="FileEditorManager"> <component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300"> <leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file pinned="false" current-in-tab="true"> <file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/README.md"> <entry file="file://$PROJECT_DIR$/README.md">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="330"> <state relative-caret-position="12735">
<caret line="22" column="92" selection-start-line="22" selection-start-column="92" selection-end-line="22" selection-end-column="92" /> <caret line="849" selection-start-line="849" selection-end-line="849" />
<folding> <folding>
<element signature="e#0#20#0" expanded="true" />
<element signature="e#0#39#0" expanded="true" /> <element signature="e#0#39#0" expanded="true" />
<element signature="e#0#39#0" expanded="true" /> <element signature="e#0#39#0" expanded="true" />
</folding> </folding>
@ -51,33 +70,33 @@
<file pinned="false" current-in-tab="false"> <file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/CHANGELOG.md"> <entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="255"> <state relative-caret-position="30">
<caret line="17" column="101" lean-forward="true" selection-start-line="17" selection-start-column="101" selection-end-line="17" selection-end-column="101" /> <caret line="2" column="145" selection-start-line="2" selection-start-column="145" selection-end-line="2" selection-end-column="145" />
</state> </state>
</provider> </provider>
</entry> </entry>
</file> </file>
<file pinned="false" current-in-tab="false"> <file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/pubspec.yaml"> <entry file="file://$PROJECT_DIR$/example/lib/main.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state> <state relative-caret-position="304">
<caret column="26" selection-start-column="6" selection-end-column="26" /> <caret line="78" column="1" selection-start-line="78" selection-start-column="1" selection-end-line="78" selection-end-column="1" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/lib/src/in_app_webview.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="235">
<caret line="1170" column="66" selection-start-line="1170" selection-start-column="66" selection-end-line="1170" selection-end-column="66" />
<folding> <folding>
<element signature="e#0#17#0" expanded="true" /> <element signature="e#0#20#0" expanded="true" />
</folding> </folding>
</state> </state>
</provider> </provider>
</entry> </entry>
</file> </file>
<file pinned="false" current-in-tab="false">
<entry file="file://$USER_HOME$/Downloads/in_app_webiew_example.screen.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="345">
<caret line="33" column="57" selection-start-line="33" selection-start-column="2" selection-end-line="33" selection-end-column="57" />
</state>
</provider>
</entry>
</file>
</leaf> </leaf>
</component> </component>
<component name="FileTemplateManagerImpl"> <component name="FileTemplateManagerImpl">
@ -90,13 +109,6 @@
</component> </component>
<component name="FindInProjectRecents"> <component name="FindInProjectRecents">
<findStrings> <findStrings>
<find>reload</find>
<find>methods</find>
<find>class</find>
<find>HttpAuthCredentialDatabase</find>
<find>###</find>
<find>#####</find>
<find>loadFile</find>
<find>window.</find> <find>window.</find>
<find>onLoadHttpError</find> <find>onLoadHttpError</find>
<find>microphone</find> <find>microphone</find>
@ -117,9 +129,16 @@
<find>[InAppBrowser]</find> <find>[InAppBrowser]</find>
<find>Response({</find> <find>Response({</find>
<find>flutter_inappbrowser</find> <find>flutter_inappbrowser</find>
<find>flutterInAppBrowserPlatformReady</find>
<find>InAppBrowser</find> <find>InAppBrowser</find>
<find>PlatformRead</find> <find>PlatformRead</find>
<find>flutterInAppBrowserPlatformReady</find>
<find>llowContentAccess</find>
<find>ppCache</find>
<find>onReceivedClientCertRequest</find>
<find>a SSL</find>
<find>iltInZoomControls</find>
<find>databaseEnabled</find>
<find>Cookie</find>
</findStrings> </findStrings>
<replaceStrings> <replaceStrings>
<replace>activity.getPreferences(0)</replace> <replace>activity.getPreferences(0)</replace>
@ -142,13 +161,10 @@
<component name="IdeDocumentHistory"> <component name="IdeDocumentHistory">
<option name="CHANGED_PATHS"> <option name="CHANGED_PATHS">
<list> <list>
<option value="$PROJECT_DIR$/.github/ISSUE_TEMPLATE/BUG_REPORT.md" />
<option value="$PROJECT_DIR$/example/test_driver/app_test.dart" />
<option value="$PROJECT_DIR$/lib/src/chrome_safari_browser.dart" /> <option value="$PROJECT_DIR$/lib/src/chrome_safari_browser.dart" />
<option value="$PROJECT_DIR$/example/assets/index.html" /> <option value="$PROJECT_DIR$/example/assets/index.html" />
<option value="$PROJECT_DIR$/lib/src/types.dart" /> <option value="$PROJECT_DIR$/lib/src/types.dart" />
<option value="$PROJECT_DIR$/example/android/app/src/main/AndroidManifest.xml" /> <option value="$PROJECT_DIR$/example/android/app/src/main/AndroidManifest.xml" />
<option value="$PROJECT_DIR$/pubspec.yaml" />
<option value="$PROJECT_DIR$/lib/flutter_inappwebview.dart" /> <option value="$PROJECT_DIR$/lib/flutter_inappwebview.dart" />
<option value="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" /> <option value="$PROJECT_DIR$/ios/flutter_inappbrowser.podspec" />
<option value="$PROJECT_DIR$/example/lib/in_app_webiew_example.screen.dart" /> <option value="$PROJECT_DIR$/example/lib/in_app_webiew_example.screen.dart" />
@ -168,7 +184,6 @@
<option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_download_start_test.dart" /> <option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_download_start_test.dart" />
<option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_find_result_received_test.dart" /> <option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_find_result_received_test.dart" />
<option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_js_dialog_test.dart" /> <option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_js_dialog_test.dart" />
<option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_load_error_test.dart" />
<option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_load_http_error_test.dart" /> <option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_load_http_error_test.dart" />
<option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_load_resource_custom_scheme_test.dart" /> <option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_load_resource_custom_scheme_test.dart" />
<option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_load_resource_test.dart" /> <option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_load_resource_test.dart" />
@ -180,23 +195,28 @@
<option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_target_blank_test.dart" /> <option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_target_blank_test.dart" />
<option value="$PROJECT_DIR$/example/test_driver/in_app_webview_should_override_url_loading_test.dart" /> <option value="$PROJECT_DIR$/example/test_driver/in_app_webview_should_override_url_loading_test.dart" />
<option value="$PROJECT_DIR$/example/test_driver/in_app_webview_ssl_request_test.dart" /> <option value="$PROJECT_DIR$/example/test_driver/in_app_webview_ssl_request_test.dart" />
<option value="$PROJECT_DIR$/lib/src/in_app_browser.dart" />
<option value="$PROJECT_DIR$/lib/src/webview_options.dart" />
<option value="$PROJECT_DIR$/example/pubspec.yaml" />
<option value="$PROJECT_DIR$/example/lib/main.dart" />
<option value="$PROJECT_DIR$/ios/Classes/InAppBrowserFlutterPlugin.m" /> <option value="$PROJECT_DIR$/ios/Classes/InAppBrowserFlutterPlugin.m" />
<option value="$PROJECT_DIR$/ios/Classes/InAppWebViewFlutterPlugin.h" /> <option value="$PROJECT_DIR$/ios/Classes/InAppWebViewFlutterPlugin.h" />
<option value="$PROJECT_DIR$/ios/Classes/SwiftFlutterPlugin.swift" /> <option value="$PROJECT_DIR$/ios/Classes/SwiftFlutterPlugin.swift" />
<option value="$PROJECT_DIR$/lib/src/in_app_webview.dart" />
<option value="$PROJECT_DIR$/lib/src/cookie_manager.dart" /> <option value="$PROJECT_DIR$/lib/src/cookie_manager.dart" />
<option value="$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart" /> <option value="$PROJECT_DIR$/lib/src/http_auth_credentials_database.dart" />
<option value="$PROJECT_DIR$/example/test_assets/in_app_webview_initial_file_test.html" /> <option value="$PROJECT_DIR$/example/test_assets/in_app_webview_initial_file_test.html" />
<option value="$PROJECT_DIR$/.git/config" />
<option value="$PROJECT_DIR$/example/test_driver/in_app_webview_on_load_error_test.dart" />
<option value="$PROJECT_DIR$/example/pubspec.yaml" />
<option value="$PROJECT_DIR$/lib/src/in_app_webview.dart" />
<option value="$PROJECT_DIR$/pubspec.yaml" />
<option value="$PROJECT_DIR$/CHANGELOG.md" /> <option value="$PROJECT_DIR$/CHANGELOG.md" />
<option value="$PROJECT_DIR$/lib/src/webview_options.dart" />
<option value="$PROJECT_DIR$/lib/src/in_app_browser.dart" />
<option value="$PROJECT_DIR$/lib/src/channel_manager.dart" />
<option value="$PROJECT_DIR$/example/lib/main.dart" />
<option value="$PROJECT_DIR$/README.md" /> <option value="$PROJECT_DIR$/README.md" />
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectFrameBounds"> <component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="-1" />
<option name="y" value="23" /> <option name="y" value="23" />
<option name="width" value="1920" /> <option name="width" value="1920" />
<option name="height" value="1057" /> <option name="height" value="1057" />
@ -248,6 +268,42 @@
<item name="flutter_inappwebview" type="462c0819:PsiDirectoryNode" /> <item name="flutter_inappwebview" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" /> <item name="example" type="462c0819:PsiDirectoryNode" />
</path> </path>
<path>
<item name="flutter_inappwebview" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappwebview" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
<item name="assets" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappwebview" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappwebview" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
<item name="assets" type="462c0819:PsiDirectoryNode" />
<item name="images" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappwebview" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappwebview" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappwebview" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappwebview" type="462c0819:PsiDirectoryNode" />
<item name="example" type="462c0819:PsiDirectoryNode" />
<item name="test_driver" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappwebview" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappwebview" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="flutter_inappwebview" type="b2602c69:ProjectViewProjectNode" />
<item name="flutter_inappwebview" type="462c0819:PsiDirectoryNode" />
<item name="lib" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
</path>
</expand> </expand>
<select /> <select />
</subPane> </subPane>
@ -284,6 +340,10 @@
<property name="show.migrate.to.gradle.popup" value="false" /> <property name="show.migrate.to.gradle.popup" value="false" />
</component> </component>
<component name="RecentsManager"> <component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/example/assets/images" />
<recent name="$PROJECT_DIR$/android/src/main/java" />
</key>
<key name="CopyFile.RECENT_KEYS"> <key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/example/test_driver" /> <recent name="$PROJECT_DIR$/example/test_driver" />
<recent name="$PROJECT_DIR$/example/test_assets" /> <recent name="$PROJECT_DIR$/example/test_assets" />
@ -291,10 +351,6 @@
<recent name="$PROJECT_DIR$/example/assets/images" /> <recent name="$PROJECT_DIR$/example/assets/images" />
<recent name="$PROJECT_DIR$/android/libs" /> <recent name="$PROJECT_DIR$/android/libs" />
</key> </key>
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/example/assets/images" />
<recent name="$PROJECT_DIR$/android/src/main/java" />
</key>
</component> </component>
<component name="RunDashboard"> <component name="RunDashboard">
<option name="ruleStates"> <option name="ruleStates">
@ -412,8 +468,7 @@
</todo-panel> </todo-panel>
</component> </component>
<component name="ToolWindowManager"> <component name="ToolWindowManager">
<frame x="0" y="23" width="1920" height="1057" extended-state="0" /> <frame x="-1" y="23" width="1920" height="1057" extended-state="6" />
<editor active="true" />
<layout> <layout>
<window_info content_ui="combo" id="Project" order="0" sideWeight="0.6177474" visible="true" weight="0.20766774" /> <window_info content_ui="combo" id="Project" order="0" sideWeight="0.6177474" visible="true" weight="0.20766774" />
<window_info id="Structure" order="1" sideWeight="0.38225257" side_tool="true" weight="0.2087327" /> <window_info id="Structure" order="1" sideWeight="0.38225257" side_tool="true" weight="0.2087327" />
@ -434,7 +489,7 @@
<window_info anchor="bottom" id="Android Profiler" order="7" show_stripe_button="false" /> <window_info anchor="bottom" id="Android Profiler" order="7" show_stripe_button="false" />
<window_info anchor="bottom" id="Event Log" order="8" sideWeight="0.50532484" side_tool="true" weight="0.35751295" /> <window_info anchor="bottom" id="Event Log" order="8" sideWeight="0.50532484" side_tool="true" weight="0.35751295" />
<window_info anchor="bottom" id="Version Control" order="9" /> <window_info anchor="bottom" id="Version Control" order="9" />
<window_info active="true" anchor="bottom" id="Terminal" order="10" sideWeight="0.4946752" visible="true" weight="0.38860103" /> <window_info active="true" anchor="bottom" id="Terminal" order="10" sideWeight="0.4946752" visible="true" weight="0.5668394" />
<window_info anchor="bottom" id="Logcat" order="11" weight="0.32953367" /> <window_info anchor="bottom" id="Logcat" order="11" weight="0.32953367" />
<window_info anchor="bottom" id="Messages" order="12" sideWeight="0.4968051" weight="0.33782384" /> <window_info anchor="bottom" id="Messages" order="12" sideWeight="0.4968051" weight="0.33782384" />
<window_info anchor="bottom" id="Dependency Viewer" order="13" weight="0.32800853" /> <window_info anchor="bottom" id="Dependency Viewer" order="13" weight="0.32800853" />
@ -464,55 +519,6 @@
</ignored-roots> </ignored-roots>
</component> </component>
<component name="editorHistoryManager"> <component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/lib/src/content_blocker.dart">
<provider selected="true" editor-type-id="text-editor">
<state>
<folding>
<element signature="e#0#41#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test.sh">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/types.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="570">
<caret line="1614" column="101" selection-start-line="1614" selection-start-column="70" selection-end-line="1614" selection-end-column="101" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/flutter_webview_example_android.iml">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/example/flutter_webview_example.iml">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/ios/flutter_inappwebview.podspec">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="60">
<caret line="4" column="44" selection-start-line="4" selection-start-column="44" selection-end-line="4" selection-end-column="44" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/lib/in_app_browser_example.screen.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="45">
<caret line="3" column="57" selection-start-line="3" selection-start-column="57" selection-end-line="3" selection-end-column="57" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/lib/chrome_safari_browser_example.screen.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="15">
<caret line="1" column="57" selection-start-line="1" selection-start-column="57" selection-end-line="1" selection-end-column="57" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/test_driver/custom_widget_test.dart"> <entry file="file://$PROJECT_DIR$/example/test_driver/custom_widget_test.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="45"> <state relative-caret-position="45">
@ -562,13 +568,6 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/example/test_driver/in_app_webview_initial_file_test.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="30">
<caret line="2" column="57" selection-start-line="2" selection-start-column="57" selection-end-line="2" selection-end-column="57" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/test_driver/in_app_webview_initial_url_test.dart"> <entry file="file://$PROJECT_DIR$/example/test_driver/in_app_webview_initial_url_test.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="30"> <state relative-caret-position="30">
@ -611,13 +610,6 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/example/test_driver/in_app_webview_on_load_error_test.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="30">
<caret line="2" column="57" selection-start-line="2" selection-start-column="57" selection-end-line="2" selection-end-column="57" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/test_driver/in_app_webview_on_load_http_error_test.dart"> <entry file="file://$PROJECT_DIR$/example/test_driver/in_app_webview_on_load_http_error_test.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="30"> <state relative-caret-position="30">
@ -688,32 +680,6 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/example/test_driver/in_app_webview_ssl_request_test.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-496">
<folding>
<element signature="e#1#40#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/webview_options.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="32">
<caret line="3" column="20" selection-start-line="3" selection-start-column="20" selection-end-line="3" selection-end-column="20" />
<folding>
<element signature="e#0#17#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/in_app_browser.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="90">
<caret line="6" column="8" selection-start-line="6" selection-start-column="8" selection-end-line="6" selection-end-column="8" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/lib/in_app_webiew_example.screen.dart"> <entry file="file://$PROJECT_DIR$/example/lib/in_app_webiew_example.screen.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="15"> <state relative-caret-position="15">
@ -721,13 +687,6 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="180">
<caret line="12" column="32" selection-start-line="12" selection-start-column="32" selection-end-line="12" selection-end-column="32" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/ios/Classes/InAppWebViewFlutterPlugin.h"> <entry file="file://$PROJECT_DIR$/ios/Classes/InAppWebViewFlutterPlugin.h">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="285"> <state relative-caret-position="285">
@ -742,16 +701,6 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/example/lib/main.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="182">
<caret line="14" column="1" lean-forward="true" selection-start-line="14" selection-start-column="1" selection-end-line="14" selection-end-column="1" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/ios/Classes/SwiftFlutterPlugin.swift"> <entry file="file://$PROJECT_DIR$/ios/Classes/SwiftFlutterPlugin.swift">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="765"> <state relative-caret-position="765">
@ -759,16 +708,6 @@
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/lib/src/in_app_webview.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="235">
<caret line="1170" column="66" selection-start-line="1170" selection-start-column="66" selection-end-line="1170" selection-end-column="66" />
<folding>
<element signature="e#0#17#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/cookie_manager.dart"> <entry file="file://$PROJECT_DIR$/lib/src/cookie_manager.dart">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="180"> <state relative-caret-position="180">
@ -804,31 +743,163 @@
<entry file="file://$PROJECT_DIR$/android/src/main/AndroidManifest.xml"> <entry file="file://$PROJECT_DIR$/android/src/main/AndroidManifest.xml">
<provider selected="true" editor-type-id="text-editor" /> <provider selected="true" editor-type-id="text-editor" />
</entry> </entry>
<entry file="file://$PROJECT_DIR$/CHANGELOG.md"> <entry file="file://$PROJECT_DIR$/.git/description">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/.git/config">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="255"> <state relative-caret-position="195">
<caret line="17" column="101" lean-forward="true" selection-start-line="17" selection-start-column="101" selection-end-line="17" selection-end-column="101" /> <caret line="13" lean-forward="true" selection-start-line="13" selection-end-line="13" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/test_driver/in_app_webview_on_load_error_test.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="357">
<caret line="27" column="33" selection-start-line="27" selection-start-column="33" selection-end-line="27" selection-end-column="33" />
<folding>
<element signature="e#0#39#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/types.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="161">
<caret line="480" column="9" selection-start-line="480" selection-start-column="9" selection-end-line="480" selection-end-column="9" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/connectivity-0.4.5+6/lib/connectivity.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="75">
<caret line="11" column="5" selection-start-line="11" selection-start-column="5" selection-end-line="11" selection-end-column="5" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/test_driver/in_app_webview_ssl_request_test.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="367">
<caret line="52" column="22" selection-start-line="50" selection-start-column="20" selection-end-line="52" selection-end-column="22" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/test_driver/in_app_webview_initial_file_test.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="96">
<caret line="28" column="85" selection-start-line="28" selection-start-column="20" selection-end-line="28" selection-end-column="85" />
<folding>
<element signature="e#0#39#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="360">
<caret line="24" column="24" selection-start-line="24" selection-start-column="24" selection-end-line="24" selection-end-column="24" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/in_app_webview.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="161">
<caret line="105" column="30" selection-start-line="105" selection-start-column="15" selection-end-line="105" selection-end-column="30" />
<folding>
<element signature="e#0#17#0" expanded="true" />
</folding>
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/pubspec.yaml"> <entry file="file://$PROJECT_DIR$/pubspec.yaml">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state> <state relative-caret-position="165">
<caret column="26" selection-start-column="6" selection-end-column="26" /> <caret line="11" column="10" selection-start-line="11" selection-start-column="10" selection-end-line="11" selection-end-column="10" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/in_app_browser.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="413">
<caret line="539" column="3" lean-forward="true" selection-start-line="539" selection-start-column="3" selection-end-line="539" selection-end-column="3" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/channel_manager.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="225">
<caret line="15" column="36" selection-start-line="15" selection-start-column="36" selection-end-line="15" selection-end-column="36" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/flutter_inappwebview.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="315">
<caret line="21" column="29" lean-forward="true" selection-start-line="21" selection-start-column="29" selection-end-line="21" selection-end-column="29" />
<folding>
<element signature="e#0#1252#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/src/webview_options.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="10620">
<caret line="708" column="23" lean-forward="true" selection-start-line="708" selection-start-column="23" selection-end-line="708" selection-end-column="23" />
<folding>
<element signature="e#0#17#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/Downloads/in_app_webiew_example.screen.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="345">
<caret line="33" column="57" selection-start-line="33" selection-start-column="2" selection-end-line="33" selection-end-column="57" />
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/README.md"> <entry file="file://$PROJECT_DIR$/README.md">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="330"> <state relative-caret-position="12735">
<caret line="22" column="92" selection-start-line="22" selection-start-column="92" selection-end-line="22" selection-end-column="92" /> <caret line="849" selection-start-line="849" selection-end-line="849" />
<folding> <folding>
<element signature="e#0#20#0" expanded="true" />
<element signature="e#0#39#0" expanded="true" /> <element signature="e#0#39#0" expanded="true" />
<element signature="e#0#39#0" expanded="true" /> <element signature="e#0#39#0" expanded="true" />
</folding> </folding>
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/CHANGELOG.md">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="30">
<caret line="2" column="145" selection-start-line="2" selection-start-column="145" selection-end-line="2" selection-end-column="145" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/example/README.md">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/example/lib/main.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="304">
<caret line="78" column="1" selection-start-line="78" selection-start-column="1" selection-end-line="78" selection-end-column="1" />
<folding>
<element signature="e#0#20#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</component> </component>
<component name="masterDetails"> <component name="masterDetails">
<states> <states>

View File

@ -1,3 +1,7 @@
## 2.0.1+1
- Fixed error "java.lang.ClassCastException: $Proxy1 cannot be cast to android.view.WindowManagerImpl" on Android when using native alert dialogs
## 2.0.1 ## 2.0.1
- Added `onPermissionRequest` event. This event is fired when the webview is requesting permission to access the specified resources and the permission currently isn't granted or denied (available only on Android). - Added `onPermissionRequest` event. This event is fired when the webview is requesting permission to access the specified resources and the permission currently isn't granted or denied (available only on Android).

View File

@ -14,7 +14,7 @@ A Flutter plugin that allows you to add an inline webview or open an in-app brow
- Dart sdk: ">=2.0.0-dev.68.0 <3.0.0" - Dart sdk: ">=2.0.0-dev.68.0 <3.0.0"
- Flutter: ">=1.9.1+hotfix.5 <2.0.0" - Flutter: ">=1.9.1+hotfix.5 <2.0.0"
- Android: `minSdkVersion 17` - Android: `minSdkVersion 17`
- iOS: `--ios-language swift` - iOS: `--ios-language swift`, Xcode version `>= 11`
### Note for Android ### Note for Android
@ -440,7 +440,7 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly:
* `onSafeBrowsingHit`: Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing (available only on Android). * `onSafeBrowsingHit`: Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing (available only on Android).
* `onReceivedHttpAuthRequest`: Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request. * `onReceivedHttpAuthRequest`: Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request.
* `onReceivedServerTrustAuthRequest`: Event fired when the WebView need to perform server trust authentication (certificate validation). * `onReceivedServerTrustAuthRequest`: Event fired when the WebView need to perform server trust authentication (certificate validation).
* `onReceivedClientCertRequest`: Notify the host application to handle a SSL client certificate request. * `onReceivedClientCertRequest`: Notify the host application to handle an SSL client certificate request.
* `onFindResultReceived`: Event fired as find-on-page operations progress. * `onFindResultReceived`: Event fired as find-on-page operations progress.
* `shouldInterceptAjaxRequest`: Event fired when an `XMLHttpRequest` is sent to a server. * `shouldInterceptAjaxRequest`: Event fired when an `XMLHttpRequest` is sent to a server.
* `onAjaxReadyStateChange`: Event fired whenever the `readyState` attribute of an `XMLHttpRequest` changes. * `onAjaxReadyStateChange`: Event fired whenever the `readyState` attribute of an `XMLHttpRequest` changes.

View File

@ -164,7 +164,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
} }
}; };
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(view.getContext(), R.style.Theme_AppCompat_Dialog_Alert); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(registrar.activeContext(), R.style.Theme_AppCompat_Dialog_Alert);
alertDialogBuilder.setMessage(alertMessage); alertDialogBuilder.setMessage(alertMessage);
if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) { if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) {
alertDialogBuilder.setPositiveButton(confirmButtonTitle, clickListener); alertDialogBuilder.setPositiveButton(confirmButtonTitle, clickListener);
@ -255,7 +255,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
} }
}; };
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(view.getContext(), R.style.Theme_AppCompat_Dialog_Alert); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(registrar.activeContext(), R.style.Theme_AppCompat_Dialog_Alert);
alertDialogBuilder.setMessage(alertMessage); alertDialogBuilder.setMessage(alertMessage);
if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) { if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) {
alertDialogBuilder.setPositiveButton(confirmButtonTitle, confirmClickListener); alertDialogBuilder.setPositiveButton(confirmButtonTitle, confirmClickListener);
@ -372,7 +372,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
} }
}; };
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(view.getContext(), R.style.Theme_AppCompat_Dialog_Alert); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(registrar.activeContext(), R.style.Theme_AppCompat_Dialog_Alert);
alertDialogBuilder.setMessage(alertMessage); alertDialogBuilder.setMessage(alertMessage);
if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) { if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) {
alertDialogBuilder.setPositiveButton(confirmButtonTitle, confirmClickListener); alertDialogBuilder.setPositiveButton(confirmButtonTitle, confirmClickListener);

View File

@ -2,9 +2,10 @@
# This is a generated file; do not edit or check into version control. # This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter" export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter"
export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example" export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example"
export "FLUTTER_TARGET=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/test_driver/app.dart" export "FLUTTER_TARGET=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/lib/main.dart"
export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios" export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "FLUTTER_FRAMEWORK_DIR=/Users/lorenzopichilli/flutter/bin/cache/artifacts/engine/ios" export "FLUTTER_FRAMEWORK_DIR=/Users/lorenzopichilli/flutter/bin/cache/artifacts/engine/ios"
export "FLUTTER_BUILD_NAME=1.0.0" export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1" export "FLUTTER_BUILD_NUMBER=1"
export "TRACK_WIDGET_CREATION=true"

View File

@ -268,6 +268,8 @@
inputPaths = ( inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework",
"${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework",
"${BUILT_PRODUCTS_DIR}/connectivity/connectivity.framework",
"${BUILT_PRODUCTS_DIR}/flutter_downloader/flutter_downloader.framework", "${BUILT_PRODUCTS_DIR}/flutter_downloader/flutter_downloader.framework",
"${BUILT_PRODUCTS_DIR}/flutter_inappwebview/flutter_inappwebview.framework", "${BUILT_PRODUCTS_DIR}/flutter_inappwebview/flutter_inappwebview.framework",
"${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework", "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework",
@ -275,6 +277,8 @@
name = "[CP] Embed Pods Frameworks"; name = "[CP] Embed Pods Frameworks";
outputPaths = ( outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_downloader.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_downloader.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappwebview.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappwebview.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework",

View File

@ -22,6 +22,7 @@ dependencies:
flutter_downloader: ^1.3.2 flutter_downloader: ^1.3.2
path_provider: ^1.4.0 path_provider: ^1.4.0
permission_handler: ^3.3.0 permission_handler: ^3.3.0
connectivity: ^0.4.5+6
flutter_inappwebview: flutter_inappwebview:
path: ../ path: ../

View File

@ -27,7 +27,7 @@ class InAppWebViewOnLoadErrorTestState extends WidgetTestState {
Expanded( Expanded(
child: Container( child: Container(
child: InAppWebView( child: InAppWebView(
initialUrl: "http://not-existing-domain.org/", initialUrl: "https://not-existing-domain.org/",
initialHeaders: {}, initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions( initialOptions: InAppWebViewWidgetOptions(
inAppWebViewOptions: InAppWebViewOptions( inAppWebViewOptions: InAppWebViewOptions(

View File

@ -6,7 +6,8 @@ import 'package:flutter/services.dart';
import 'types.dart' show ListenerCallback; import 'types.dart' show ListenerCallback;
class ChannelManager { class ChannelManager {
static const MethodChannel channel = const MethodChannel('com.pichillilorenzo/flutter_inappbrowser'); static const MethodChannel channel =
const MethodChannel('com.pichillilorenzo/flutter_inappbrowser');
static bool initialized = false; static bool initialized = false;
static final listeners = HashMap<String, ListenerCallback>(); static final listeners = HashMap<String, ListenerCallback>();
@ -16,13 +17,12 @@ class ChannelManager {
} }
static void addListener(String key, ListenerCallback callback) { static void addListener(String key, ListenerCallback callback) {
if (!initialized) if (!initialized) init();
init();
listeners.putIfAbsent(key, () => callback); listeners.putIfAbsent(key, () => callback);
} }
static void init () { static void init() {
channel.setMethodCallHandler(_handleMethod); channel.setMethodCallHandler(_handleMethod);
initialized = true; initialized = true;
} }
} }

View File

@ -20,7 +20,7 @@ class ChromeSafariBrowser {
bool _isOpened = false; bool _isOpened = false;
///Initialize the [ChromeSafariBrowser] instance with an [InAppBrowser] fallback instance or `null`. ///Initialize the [ChromeSafariBrowser] instance with an [InAppBrowser] fallback instance or `null`.
ChromeSafariBrowser ({bFallback}) { ChromeSafariBrowser({bFallback}) {
uuid = uuidGenerator.v4(); uuid = uuidGenerator.v4();
browserFallback = bFallback; browserFallback = bFallback;
ChannelManager.addListener(uuid, handleMethod); ChannelManager.addListener(uuid, handleMethod);
@ -28,7 +28,7 @@ class ChromeSafariBrowser {
} }
Future<dynamic> handleMethod(MethodCall call) async { Future<dynamic> handleMethod(MethodCall call) async {
switch(call.method) { switch (call.method) {
case "onChromeSafariBrowserOpened": case "onChromeSafariBrowserOpened":
onOpened(); onOpened();
break; break;
@ -53,7 +53,11 @@ class ChromeSafariBrowser {
///[headersFallback]: The additional header of the [InAppBrowser] instance fallback to be used in the HTTP request for this URL, specified as a map from name to value. ///[headersFallback]: The additional header of the [InAppBrowser] instance fallback to be used in the HTTP request for this URL, specified as a map from name to value.
/// ///
///[optionsFallback]: Options used by the [InAppBrowser] instance fallback. ///[optionsFallback]: Options used by the [InAppBrowser] instance fallback.
Future<void> open({@required String url, ChromeSafariBrowserClassOptions options, Map<String, String> headersFallback = const {}, InAppBrowserClassOptions optionsFallback}) async { Future<void> open(
{@required String url,
ChromeSafariBrowserClassOptions options,
Map<String, String> headersFallback = const {},
InAppBrowserClassOptions optionsFallback}) async {
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
this.throwIsAlreadyOpened(message: 'Cannot open $url!'); this.throwIsAlreadyOpened(message: 'Cannot open $url!');
@ -65,21 +69,33 @@ class ChromeSafariBrowser {
Map<String, dynamic> optionsFallbackMap = {}; Map<String, dynamic> optionsFallbackMap = {};
if (optionsFallback != null) { if (optionsFallback != null) {
optionsFallbackMap.addAll(optionsFallback.inAppBrowserOptions?.toMap() ?? {}); optionsFallbackMap
optionsFallbackMap.addAll(optionsFallback.inAppWebViewWidgetOptions?.inAppWebViewOptions?.toMap() ?? {}); .addAll(optionsFallback.inAppBrowserOptions?.toMap() ?? {});
optionsFallbackMap.addAll(optionsFallback
.inAppWebViewWidgetOptions?.inAppWebViewOptions
?.toMap() ??
{});
if (Platform.isAndroid) { if (Platform.isAndroid) {
optionsFallbackMap.addAll(optionsFallback.androidInAppBrowserOptions?.toMap() ?? {}); optionsFallbackMap
optionsFallbackMap.addAll(optionsFallback.inAppWebViewWidgetOptions?.androidInAppWebViewOptions?.toMap() ?? {}); .addAll(optionsFallback.androidInAppBrowserOptions?.toMap() ?? {});
} optionsFallbackMap.addAll(optionsFallback
else if (Platform.isIOS) { .inAppWebViewWidgetOptions?.androidInAppWebViewOptions
optionsFallbackMap.addAll(optionsFallback.iosInAppBrowserOptions?.toMap() ?? {}); ?.toMap() ??
optionsFallbackMap.addAll(optionsFallback.inAppWebViewWidgetOptions?.iosInAppWebViewOptions?.toMap() ?? {}); {});
} else if (Platform.isIOS) {
optionsFallbackMap
.addAll(optionsFallback.iosInAppBrowserOptions?.toMap() ?? {});
optionsFallbackMap.addAll(optionsFallback
.inAppWebViewWidgetOptions?.iosInAppWebViewOptions
?.toMap() ??
{});
} }
} }
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('uuid', () => uuid); args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('uuidFallback', () => (browserFallback != null) ? browserFallback.uuid : ''); args.putIfAbsent('uuidFallback',
() => (browserFallback != null) ? browserFallback.uuid : '');
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
args.putIfAbsent('headers', () => headersFallback); args.putIfAbsent('headers', () => headersFallback);
args.putIfAbsent('options', () => optionsMap); args.putIfAbsent('options', () => optionsMap);
@ -91,19 +107,13 @@ class ChromeSafariBrowser {
} }
///Event fires when the [ChromeSafariBrowser] is opened. ///Event fires when the [ChromeSafariBrowser] is opened.
void onOpened() { void onOpened() {}
}
///Event fires when the [ChromeSafariBrowser] is loaded. ///Event fires when the [ChromeSafariBrowser] is loaded.
void onLoaded() { void onLoaded() {}
}
///Event fires when the [ChromeSafariBrowser] is closed. ///Event fires when the [ChromeSafariBrowser] is closed.
void onClosed() { void onClosed() {}
}
///Returns `true` if the [ChromeSafariBrowser] instance is opened, otherwise `false`. ///Returns `true` if the [ChromeSafariBrowser] instance is opened, otherwise `false`.
bool isOpened() { bool isOpened() {
@ -112,13 +122,17 @@ class ChromeSafariBrowser {
void throwIsAlreadyOpened({String message = ''}) { void throwIsAlreadyOpened({String message = ''}) {
if (this.isOpened()) { if (this.isOpened()) {
throw Exception(['Error: ${ (message.isEmpty) ? '' : message + ' '}The browser is already opened.']); throw Exception([
'Error: ${(message.isEmpty) ? '' : message + ' '}The browser is already opened.'
]);
} }
} }
void throwIsNotOpened({String message = ''}) { void throwIsNotOpened({String message = ''}) {
if (!this.isOpened()) { if (!this.isOpened()) {
throw Exception(['Error: ${ (message.isEmpty) ? '' : message + ' '}The browser is not opened.']); throw Exception([
'Error: ${(message.isEmpty) ? '' : message + ' '}The browser is not opened.'
]);
} }
} }
} }

View File

@ -11,27 +11,22 @@ import 'types.dart';
class ContentBlocker { class ContentBlocker {
///Trigger of the content blocker. The trigger tells to the WebView when to perform the corresponding action. ///Trigger of the content blocker. The trigger tells to the WebView when to perform the corresponding action.
ContentBlockerTrigger trigger; ContentBlockerTrigger trigger;
///Action associated to the trigger. The action tells to the WebView what to do when the trigger is matched. ///Action associated to the trigger. The action tells to the WebView what to do when the trigger is matched.
ContentBlockerAction action; ContentBlockerAction action;
ContentBlocker({@required this.trigger,@required this.action}); ContentBlocker({@required this.trigger, @required this.action});
Map<String, Map<String, dynamic>> toMap() { Map<String, Map<String, dynamic>> toMap() {
return { return {"trigger": trigger.toMap(), "action": action.toMap()};
"trigger": trigger.toMap(),
"action": action.toMap()
};
} }
static ContentBlocker fromMap(Map<dynamic, Map<dynamic, dynamic>> map) { static ContentBlocker fromMap(Map<dynamic, Map<dynamic, dynamic>> map) {
return ContentBlocker( return ContentBlocker(
trigger: ContentBlockerTrigger.fromMap( trigger: ContentBlockerTrigger.fromMap(
Map<String, dynamic>.from(map["trigger"]) Map<String, dynamic>.from(map["trigger"])),
),
action: ContentBlockerAction.fromMap( action: ContentBlockerAction.fromMap(
Map<String, dynamic>.from(map["action"]) Map<String, dynamic>.from(map["action"])));
)
);
} }
} }
@ -42,29 +37,42 @@ class ContentBlocker {
class ContentBlockerTrigger { class ContentBlockerTrigger {
///A regular expression pattern to match the URL against. ///A regular expression pattern to match the URL against.
String urlFilter; String urlFilter;
///Used only by iOS. A Boolean value. The default value is false. ///Used only by iOS. A Boolean value. The default value is false.
bool urlFilterIsCaseSensitive; bool urlFilterIsCaseSensitive;
///A list of [ContentBlockerTriggerResourceType] representing the resource types (how the browser intends to use the resource) that the rule should match. ///A list of [ContentBlockerTriggerResourceType] representing the resource types (how the browser intends to use the resource) that the rule should match.
///If not specified, the rule matches all resource types. ///If not specified, the rule matches all resource types.
List<ContentBlockerTriggerResourceType> resourceType; List<ContentBlockerTriggerResourceType> resourceType;
///A list of strings matched to a URL's domain; limits action to a list of specific domains. ///A list of strings matched to a URL's domain; limits action to a list of specific domains.
///Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.unlessDomain]. ///Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.unlessDomain].
List<String> ifDomain; List<String> ifDomain;
///A list of strings matched to a URL's domain; acts on any site except domains in a provided list. ///A list of strings matched to a URL's domain; acts on any site except domains in a provided list.
///Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.ifDomain]. ///Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain and subdomains. Can't be used with [ContentBlockerTrigger.ifDomain].
List<String> unlessDomain; List<String> unlessDomain;
///A list of [ContentBlockerTriggerLoadType] that can include one of two mutually exclusive values. If not specified, the rule matches all load types. ///A list of [ContentBlockerTriggerLoadType] that can include one of two mutually exclusive values. If not specified, the rule matches all load types.
List<ContentBlockerTriggerLoadType> loadType; List<ContentBlockerTriggerLoadType> loadType;
///A list of strings matched to the entire main document URL; limits the action to a specific list of URL patterns. ///A list of strings matched to the entire main document URL; limits the action to a specific list of URL patterns.
///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.unlessTopUrl]. ///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.unlessTopUrl].
List<String> ifTopUrl; List<String> ifTopUrl;
///An array of strings matched to the entire main document URL; acts on any site except URL patterns in provided list. ///An array of strings matched to the entire main document URL; acts on any site except URL patterns in provided list.
///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.ifTopUrl]. ///Values must be lowercase ASCII, or punycode for non-ASCII. Can't be used with [ContentBlockerTrigger.ifTopUrl].
List<String> unlessTopUrl; List<String> unlessTopUrl;
ContentBlockerTrigger({@required String urlFilter, bool urlFilterIsCaseSensitive = false, List<ContentBlockerTriggerResourceType> resourceType = const [], ContentBlockerTrigger(
List<String> ifDomain = const [], List<String> unlessDomain = const [], List<ContentBlockerTriggerLoadType> loadType = const [], {@required String urlFilter,
List<String> ifTopUrl = const [], List<String> unlessTopUrl = const []}) { bool urlFilterIsCaseSensitive = false,
List<ContentBlockerTriggerResourceType> resourceType = const [],
List<String> ifDomain = const [],
List<String> unlessDomain = const [],
List<ContentBlockerTriggerLoadType> loadType = const [],
List<String> ifTopUrl = const [],
List<String> unlessTopUrl = const []}) {
this.urlFilter = urlFilter; this.urlFilter = urlFilter;
assert(this.urlFilter != null); assert(this.urlFilter != null);
this.resourceType = resourceType; this.resourceType = resourceType;
@ -101,7 +109,9 @@ class ContentBlockerTrigger {
}; };
map.keys map.keys
.where((key) => map[key] == null || (map[key] is List && (map[key] as List).length == 0)) // filter keys .where((key) =>
map[key] == null ||
(map[key] is List && (map[key] as List).length == 0)) // filter keys
.toList() // create a copy to avoid concurrent modifications .toList() // create a copy to avoid concurrent modifications
.forEach(map.remove); .forEach(map.remove);
@ -112,7 +122,8 @@ class ContentBlockerTrigger {
List<ContentBlockerTriggerResourceType> resourceType = []; List<ContentBlockerTriggerResourceType> resourceType = [];
List<ContentBlockerTriggerLoadType> loadType = []; List<ContentBlockerTriggerLoadType> loadType = [];
List<String> resourceTypeStringList = List<String>.from(map["resource-type"] ?? []); List<String> resourceTypeStringList =
List<String>.from(map["resource-type"] ?? []);
resourceTypeStringList.forEach((type) { resourceTypeStringList.forEach((type) {
resourceType.add(ContentBlockerTriggerResourceType.fromValue(type)); resourceType.add(ContentBlockerTriggerResourceType.fromValue(type));
}); });
@ -130,8 +141,7 @@ class ContentBlockerTrigger {
resourceType: resourceType, resourceType: resourceType,
loadType: loadType, loadType: loadType,
ifTopUrl: List<String>.from(map["if-top-url"] ?? []), ifTopUrl: List<String>.from(map["if-top-url"] ?? []),
unlessTopUrl: List<String>.from(map["unless-top-url"] ?? []) unlessTopUrl: List<String>.from(map["unless-top-url"] ?? []));
);
} }
} }
@ -143,11 +153,13 @@ class ContentBlockerTrigger {
class ContentBlockerAction { class ContentBlockerAction {
///Type of the action. ///Type of the action.
ContentBlockerActionType type; ContentBlockerActionType type;
///If the action type is [ContentBlockerActionType.CSS_DISPLAY_NONE], then also the [selector] property is required, otherwise it is ignored. ///If the action type is [ContentBlockerActionType.CSS_DISPLAY_NONE], then also the [selector] property is required, otherwise it is ignored.
///It specify a string that defines a selector list. Use CSS identifiers as the individual selector values, separated by commas. ///It specify a string that defines a selector list. Use CSS identifiers as the individual selector values, separated by commas.
String selector; String selector;
ContentBlockerAction({@required ContentBlockerActionType type, String selector}) { ContentBlockerAction(
{@required ContentBlockerActionType type, String selector}) {
this.type = type; this.type = type;
assert(this.type != null); assert(this.type != null);
if (this.type == ContentBlockerActionType.CSS_DISPLAY_NONE) { if (this.type == ContentBlockerActionType.CSS_DISPLAY_NONE) {
@ -157,13 +169,12 @@ class ContentBlockerAction {
} }
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
Map<String, dynamic> map = { Map<String, dynamic> map = {"type": type.toValue(), "selector": selector};
"type": type.toValue(),
"selector": selector
};
map.keys map.keys
.where((key) => map[key] == null || (map[key] is List && (map[key] as List).length == 0)) // filter keys .where((key) =>
map[key] == null ||
(map[key] is List && (map[key] as List).length == 0)) // filter keys
.toList() // create a copy to avoid concurrent modifications .toList() // create a copy to avoid concurrent modifications
.forEach(map.remove); .forEach(map.remove);
@ -173,7 +184,6 @@ class ContentBlockerAction {
static ContentBlockerAction fromMap(Map<String, dynamic> map) { static ContentBlockerAction fromMap(Map<String, dynamic> map) {
return ContentBlockerAction( return ContentBlockerAction(
type: ContentBlockerActionType.fromValue(map["type"]), type: ContentBlockerActionType.fromValue(map["type"]),
selector: map["selector"] selector: map["selector"]);
);
} }
} }

View File

@ -10,7 +10,8 @@ import 'types.dart';
///**NOTE for iOS**: available from iOS 11.0+. ///**NOTE for iOS**: available from iOS 11.0+.
class CookieManager { class CookieManager {
static CookieManager _instance; static CookieManager _instance;
static const MethodChannel _channel = const MethodChannel('com.pichillilorenzo/flutter_inappwebview_cookiemanager'); static const MethodChannel _channel = const MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_cookiemanager');
///Gets the cookie manager shared instance. ///Gets the cookie manager shared instance.
static CookieManager instance() { static CookieManager instance() {
@ -23,21 +24,22 @@ class CookieManager {
return _instance; return _instance;
} }
static Future<dynamic> _handleMethod(MethodCall call) async { static Future<dynamic> _handleMethod(MethodCall call) async {}
}
///Sets a cookie for the given [url]. Any existing cookie with the same [host], [path] and [name] will be replaced with the new cookie. The cookie being set will be ignored if it is expired. ///Sets a cookie for the given [url]. Any existing cookie with the same [host], [path] and [name] will be replaced with the new cookie. The cookie being set will be ignored if it is expired.
/// ///
///The default value of [path] is `"/"`. ///The default value of [path] is `"/"`.
///If [domain] is `null`, its default value will be the domain name of [url]. ///If [domain] is `null`, its default value will be the domain name of [url].
Future<void> setCookie({@required String url, @required String name, @required String value, Future<void> setCookie(
String domain, {@required String url,
String path = "/", @required String name,
int expiresDate, @required String value,
int maxAge, String domain,
bool isSecure }) async { String path = "/",
if (domain == null) int expiresDate,
domain = _getDomainName(url); int maxAge,
bool isSecure}) async {
if (domain == null) domain = _getDomainName(url);
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
assert(name != null && name.isNotEmpty); assert(name != null && name.isNotEmpty);
@ -64,17 +66,20 @@ class CookieManager {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
List<dynamic> cookieListMap = await _channel.invokeMethod('getCookies', args); List<dynamic> cookieListMap =
await _channel.invokeMethod('getCookies', args);
cookieListMap = cookieListMap.cast<Map<dynamic, dynamic>>(); cookieListMap = cookieListMap.cast<Map<dynamic, dynamic>>();
List<Cookie> cookies = []; List<Cookie> cookies = [];
for(var i = 0; i < cookieListMap.length; i++) { for (var i = 0; i < cookieListMap.length; i++) {
cookies.add(Cookie(name: cookieListMap[i]["name"], value: cookieListMap[i]["value"])); cookies.add(Cookie(
name: cookieListMap[i]["name"], value: cookieListMap[i]["value"]));
} }
return cookies; return cookies;
} }
///Gets a cookie by its [name] for the given [url]. ///Gets a cookie by its [name] for the given [url].
Future<Cookie> getCookie({@required String url, @required String name}) async { Future<Cookie> getCookie(
{@required String url, @required String name}) async {
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
assert(name != null && name.isNotEmpty); assert(name != null && name.isNotEmpty);
@ -82,7 +87,7 @@ class CookieManager {
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
List<dynamic> cookies = await _channel.invokeMethod('getCookies', args); List<dynamic> cookies = await _channel.invokeMethod('getCookies', args);
cookies = cookies.cast<Map<dynamic, dynamic>>(); cookies = cookies.cast<Map<dynamic, dynamic>>();
for(var i = 0; i < cookies.length; i++) { for (var i = 0; i < cookies.length; i++) {
cookies[i] = cookies[i].cast<String, dynamic>(); cookies[i] = cookies[i].cast<String, dynamic>();
if (cookies[i]["name"] == name) if (cookies[i]["name"] == name)
return Cookie(name: cookies[i]["name"], value: cookies[i]["value"]); return Cookie(name: cookies[i]["name"], value: cookies[i]["value"]);
@ -94,9 +99,12 @@ class CookieManager {
/// ///
///The default value of [path] is `"/"`. ///The default value of [path] is `"/"`.
///If [domain] is `null` or empty, its default value will be the domain name of [url]. ///If [domain] is `null` or empty, its default value will be the domain name of [url].
Future<void> deleteCookie({@required String url, @required String name, String domain = "", String path = "/"}) async { Future<void> deleteCookie(
if (domain == null || domain.isEmpty) {@required String url,
domain = _getDomainName(url); @required String name,
String domain = "",
String path = "/"}) async {
if (domain == null || domain.isEmpty) domain = _getDomainName(url);
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
assert(name != null && name.isNotEmpty); assert(name != null && name.isNotEmpty);
@ -115,9 +123,9 @@ class CookieManager {
/// ///
///The default value of [path] is `"/"`. ///The default value of [path] is `"/"`.
///If [domain] is `null` or empty, its default value will be the domain name of [url]. ///If [domain] is `null` or empty, its default value will be the domain name of [url].
Future<void> deleteCookies({@required String url, String domain = "", String path = "/"}) async { Future<void> deleteCookies(
if (domain == null || domain.isEmpty) {@required String url, String domain = "", String path = "/"}) async {
domain = _getDomainName(url); if (domain == null || domain.isEmpty) domain = _getDomainName(url);
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
assert(domain != null && url.isNotEmpty); assert(domain != null && url.isNotEmpty);
@ -139,8 +147,7 @@ class CookieManager {
String _getDomainName(String url) { String _getDomainName(String url) {
Uri uri = Uri.parse(url); Uri uri = Uri.parse(url);
String domain = uri.host; String domain = uri.host;
if (domain == null) if (domain == null) return "";
return "";
return domain.startsWith("www.") ? domain.substring(4) : domain; return domain.startsWith("www.") ? domain.substring(4) : domain;
} }
} }

View File

@ -11,7 +11,8 @@ import 'package:flutter/services.dart';
///doesn't offer the same functionalities as iOS `URLCredentialStorage`. ///doesn't offer the same functionalities as iOS `URLCredentialStorage`.
class HttpAuthCredentialDatabase { class HttpAuthCredentialDatabase {
static HttpAuthCredentialDatabase _instance; static HttpAuthCredentialDatabase _instance;
static const MethodChannel _channel = const MethodChannel('com.pichillilorenzo/flutter_inappwebview_credential_database'); static const MethodChannel _channel = const MethodChannel(
'com.pichillilorenzo/flutter_inappwebview_credential_database');
///Gets the database shared instance. ///Gets the database shared instance.
static HttpAuthCredentialDatabase instance() { static HttpAuthCredentialDatabase instance() {
@ -24,44 +25,57 @@ class HttpAuthCredentialDatabase {
return _instance; return _instance;
} }
static Future<dynamic> _handleMethod(MethodCall call) async { static Future<dynamic> _handleMethod(MethodCall call) async {}
}
///Gets a map list of all HTTP auth credentials saved. ///Gets a map list of all HTTP auth credentials saved.
///Each map contains the key `protectionSpace` of type [ProtectionSpace] ///Each map contains the key `protectionSpace` of type [ProtectionSpace]
///and the key `credentials` of type `List<HttpAuthCredential>` that contains all the HTTP auth credentials saved for that `protectionSpace`. ///and the key `credentials` of type `List<HttpAuthCredential>` that contains all the HTTP auth credentials saved for that `protectionSpace`.
Future<List<Map<String, dynamic>>> getAllAuthCredentials() async { Future<List<Map<String, dynamic>>> getAllAuthCredentials() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
List<dynamic> allCredentials = await _channel.invokeMethod('getAllAuthCredentials', args); List<dynamic> allCredentials =
await _channel.invokeMethod('getAllAuthCredentials', args);
List<Map<String, dynamic>> result = []; List<Map<String, dynamic>> result = [];
for (Map<dynamic, dynamic> map in allCredentials) { for (Map<dynamic, dynamic> map in allCredentials) {
Map<dynamic, dynamic> protectionSpace = map["protectionSpace"]; Map<dynamic, dynamic> protectionSpace = map["protectionSpace"];
List<dynamic> credentials = map["credentials"]; List<dynamic> credentials = map["credentials"];
result.add({ result.add({
"protectionSpace": ProtectionSpace(host: protectionSpace["host"], protocol: protectionSpace["protocol"], realm: protectionSpace["realm"], port: protectionSpace["port"]), "protectionSpace": ProtectionSpace(
"credentials": credentials.map((credential) => HttpAuthCredential(username: credential["username"], password: credential["password"])).toList() host: protectionSpace["host"],
protocol: protectionSpace["protocol"],
realm: protectionSpace["realm"],
port: protectionSpace["port"]),
"credentials": credentials
.map((credential) => HttpAuthCredential(
username: credential["username"],
password: credential["password"]))
.toList()
}); });
} }
return result; return result;
} }
///Gets all the HTTP auth credentials saved for that [protectionSpace]. ///Gets all the HTTP auth credentials saved for that [protectionSpace].
Future<List<HttpAuthCredential>> getHttpAuthCredentials({@required ProtectionSpace protectionSpace}) async { Future<List<HttpAuthCredential>> getHttpAuthCredentials(
{@required ProtectionSpace protectionSpace}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("host", () => protectionSpace.host); args.putIfAbsent("host", () => protectionSpace.host);
args.putIfAbsent("protocol", () => protectionSpace.protocol); args.putIfAbsent("protocol", () => protectionSpace.protocol);
args.putIfAbsent("realm", () => protectionSpace.realm); args.putIfAbsent("realm", () => protectionSpace.realm);
args.putIfAbsent("port", () => protectionSpace.port); args.putIfAbsent("port", () => protectionSpace.port);
List<dynamic> credentialList = await _channel.invokeMethod('getHttpAuthCredentials', args); List<dynamic> credentialList =
await _channel.invokeMethod('getHttpAuthCredentials', args);
List<HttpAuthCredential> credentials = []; List<HttpAuthCredential> credentials = [];
for (Map<dynamic, dynamic> credential in credentialList) { for (Map<dynamic, dynamic> credential in credentialList) {
credentials.add(HttpAuthCredential(username: credential["username"], password: credential["password"])); credentials.add(HttpAuthCredential(
username: credential["username"], password: credential["password"]));
} }
return credentials; return credentials;
} }
///Saves an HTTP auth [credential] for that [protectionSpace]. ///Saves an HTTP auth [credential] for that [protectionSpace].
Future<void> setHttpAuthCredential({@required ProtectionSpace protectionSpace, @required HttpAuthCredential credential}) async { Future<void> setHttpAuthCredential(
{@required ProtectionSpace protectionSpace,
@required HttpAuthCredential credential}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("host", () => protectionSpace.host); args.putIfAbsent("host", () => protectionSpace.host);
args.putIfAbsent("protocol", () => protectionSpace.protocol); args.putIfAbsent("protocol", () => protectionSpace.protocol);
@ -73,7 +87,9 @@ class HttpAuthCredentialDatabase {
} }
///Removes an HTTP auth [credential] for that [protectionSpace]. ///Removes an HTTP auth [credential] for that [protectionSpace].
Future<void> removeHttpAuthCredential({@required ProtectionSpace protectionSpace, @required HttpAuthCredential credential}) async { Future<void> removeHttpAuthCredential(
{@required ProtectionSpace protectionSpace,
@required HttpAuthCredential credential}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("host", () => protectionSpace.host); args.putIfAbsent("host", () => protectionSpace.host);
args.putIfAbsent("protocol", () => protectionSpace.protocol); args.putIfAbsent("protocol", () => protectionSpace.protocol);
@ -85,7 +101,8 @@ class HttpAuthCredentialDatabase {
} }
///Removes all the HTTP auth credentials saved for that [protectionSpace]. ///Removes all the HTTP auth credentials saved for that [protectionSpace].
Future<void> removeHttpAuthCredentials({@required ProtectionSpace protectionSpace}) async { Future<void> removeHttpAuthCredentials(
{@required ProtectionSpace protectionSpace}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent("host", () => protectionSpace.host); args.putIfAbsent("host", () => protectionSpace.host);
args.putIfAbsent("protocol", () => protectionSpace.protocol); args.putIfAbsent("protocol", () => protectionSpace.protocol);
@ -99,4 +116,4 @@ class HttpAuthCredentialDatabase {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
await _channel.invokeMethod('clearAllAuthCredentials', args); await _channel.invokeMethod('clearAllAuthCredentials', args);
} }
} }

View File

@ -14,23 +14,25 @@ import 'in_app_webview.dart' show InAppWebViewController;
/// ///
///This class uses the native WebView of the platform. ///This class uses the native WebView of the platform.
class InAppBrowser { class InAppBrowser {
String uuid; String uuid;
Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap = HashMap<String, JavaScriptHandlerCallback>(); Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap =
HashMap<String, JavaScriptHandlerCallback>();
bool _isOpened = false; bool _isOpened = false;
/// WebView Controller that can be used to access the [InAppWebView] API. /// WebView Controller that can be used to access the [InAppWebView] API.
InAppWebViewController webViewController; InAppWebViewController webViewController;
/// ///
InAppBrowser () { InAppBrowser() {
uuid = uuidGenerator.v4(); uuid = uuidGenerator.v4();
ChannelManager.addListener(uuid, handleMethod); ChannelManager.addListener(uuid, handleMethod);
_isOpened = false; _isOpened = false;
webViewController = new InAppWebViewController.fromInAppBrowser(uuid, ChannelManager.channel, this); webViewController = new InAppWebViewController.fromInAppBrowser(
uuid, ChannelManager.channel, this);
} }
Future<dynamic> handleMethod(MethodCall call) async { Future<dynamic> handleMethod(MethodCall call) async {
switch(call.method) { switch (call.method) {
case "onBrowserCreated": case "onBrowserCreated":
this._isOpened = true; this._isOpened = true;
onBrowserCreated(); onBrowserCreated();
@ -51,21 +53,29 @@ class InAppBrowser {
///[headers]: The additional headers to be used in the HTTP request for this URL, specified as a map from name to value. ///[headers]: The additional headers to be used in the HTTP request for this URL, specified as a map from name to value.
/// ///
///[options]: Options for the [InAppBrowser]. ///[options]: Options for the [InAppBrowser].
Future<void> open({String url = "about:blank", Map<String, String> headers = const {}, InAppBrowserClassOptions options}) async { Future<void> open(
{String url = "about:blank",
Map<String, String> headers = const {},
InAppBrowserClassOptions options}) async {
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 = {}; Map<String, dynamic> optionsMap = {};
optionsMap.addAll(options.inAppBrowserOptions?.toMap() ?? {}); optionsMap.addAll(options.inAppBrowserOptions?.toMap() ?? {});
optionsMap.addAll(options.inAppWebViewWidgetOptions?.inAppWebViewOptions?.toMap() ?? {}); optionsMap.addAll(
options.inAppWebViewWidgetOptions?.inAppWebViewOptions?.toMap() ?? {});
if (Platform.isAndroid) { if (Platform.isAndroid) {
optionsMap.addAll(options.androidInAppBrowserOptions?.toMap() ?? {}); optionsMap.addAll(options.androidInAppBrowserOptions?.toMap() ?? {});
optionsMap.addAll(options.inAppWebViewWidgetOptions?.androidInAppWebViewOptions?.toMap() ?? {}); optionsMap.addAll(options
} .inAppWebViewWidgetOptions?.androidInAppWebViewOptions
else if (Platform.isIOS) { ?.toMap() ??
{});
} else if (Platform.isIOS) {
optionsMap.addAll(options.iosInAppBrowserOptions?.toMap() ?? {}); optionsMap.addAll(options.iosInAppBrowserOptions?.toMap() ?? {});
optionsMap.addAll(options.inAppWebViewWidgetOptions?.iosInAppWebViewOptions?.toMap() ?? {}); optionsMap.addAll(
options.inAppWebViewWidgetOptions?.iosInAppWebViewOptions?.toMap() ??
{});
} }
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
@ -113,21 +123,29 @@ class InAppBrowser {
///[headers]: The additional headers to be used in the HTTP request for this URL, specified as a map from name to value. ///[headers]: The additional headers to be used in the HTTP request for this URL, specified as a map from name to value.
/// ///
///[options]: Options for the [InAppBrowser]. ///[options]: Options for the [InAppBrowser].
Future<void> openFile({@required String assetFilePath, Map<String, String> headers = const {}, InAppBrowserClassOptions options}) async { Future<void> openFile(
{@required String assetFilePath,
Map<String, String> headers = const {},
InAppBrowserClassOptions options}) async {
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 = {}; Map<String, dynamic> optionsMap = {};
optionsMap.addAll(options.inAppBrowserOptions?.toMap() ?? {}); optionsMap.addAll(options.inAppBrowserOptions?.toMap() ?? {});
optionsMap.addAll(options.inAppWebViewWidgetOptions?.inAppWebViewOptions?.toMap() ?? {}); optionsMap.addAll(
options.inAppWebViewWidgetOptions?.inAppWebViewOptions?.toMap() ?? {});
if (Platform.isAndroid) { if (Platform.isAndroid) {
optionsMap.addAll(options.androidInAppBrowserOptions?.toMap() ?? {}); optionsMap.addAll(options.androidInAppBrowserOptions?.toMap() ?? {});
optionsMap.addAll(options.inAppWebViewWidgetOptions?.androidInAppWebViewOptions?.toMap() ?? {}); optionsMap.addAll(options
} .inAppWebViewWidgetOptions?.androidInAppWebViewOptions
else if (Platform.isIOS) { ?.toMap() ??
{});
} else if (Platform.isIOS) {
optionsMap.addAll(options.iosInAppBrowserOptions?.toMap() ?? {}); optionsMap.addAll(options.iosInAppBrowserOptions?.toMap() ?? {});
optionsMap.addAll(options.inAppWebViewWidgetOptions?.iosInAppWebViewOptions?.toMap() ?? {}); optionsMap.addAll(
options.inAppWebViewWidgetOptions?.iosInAppWebViewOptions?.toMap() ??
{});
} }
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
@ -149,20 +167,30 @@ class InAppBrowser {
///The [encoding] parameter specifies the encoding of the data. ///The [encoding] parameter specifies the encoding of the data.
/// ///
///The [options] parameter specifies the options for the [InAppBrowser]. ///The [options] parameter specifies the options for the [InAppBrowser].
Future<void> openData({@required String data, String mimeType = "text/html", String encoding = "utf8", String baseUrl = "about:blank", InAppBrowserClassOptions options}) async { Future<void> openData(
{@required String data,
String mimeType = "text/html",
String encoding = "utf8",
String baseUrl = "about:blank",
InAppBrowserClassOptions options}) async {
assert(data != null); assert(data != null);
Map<String, dynamic> optionsMap = {}; Map<String, dynamic> optionsMap = {};
optionsMap.addAll(options.inAppBrowserOptions?.toMap() ?? {}); optionsMap.addAll(options.inAppBrowserOptions?.toMap() ?? {});
optionsMap.addAll(options.inAppWebViewWidgetOptions?.inAppWebViewOptions?.toMap() ?? {}); optionsMap.addAll(
options.inAppWebViewWidgetOptions?.inAppWebViewOptions?.toMap() ?? {});
if (Platform.isAndroid) { if (Platform.isAndroid) {
optionsMap.addAll(options.androidInAppBrowserOptions?.toMap() ?? {}); optionsMap.addAll(options.androidInAppBrowserOptions?.toMap() ?? {});
optionsMap.addAll(options.inAppWebViewWidgetOptions?.androidInAppWebViewOptions?.toMap() ?? {}); optionsMap.addAll(options
} .inAppWebViewWidgetOptions?.androidInAppWebViewOptions
else if (Platform.isIOS) { ?.toMap() ??
{});
} else if (Platform.isIOS) {
optionsMap.addAll(options.iosInAppBrowserOptions?.toMap() ?? {}); optionsMap.addAll(options.iosInAppBrowserOptions?.toMap() ?? {});
optionsMap.addAll(options.inAppWebViewWidgetOptions?.iosInAppWebViewOptions?.toMap() ?? {}); optionsMap.addAll(
options.inAppWebViewWidgetOptions?.iosInAppWebViewOptions?.toMap() ??
{});
} }
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
@ -233,14 +261,19 @@ class InAppBrowser {
Map<String, dynamic> optionsMap = {}; Map<String, dynamic> optionsMap = {};
optionsMap.addAll(options.inAppBrowserOptions?.toMap() ?? {}); optionsMap.addAll(options.inAppBrowserOptions?.toMap() ?? {});
optionsMap.addAll(options.inAppWebViewWidgetOptions?.inAppWebViewOptions?.toMap() ?? {}); optionsMap.addAll(
options.inAppWebViewWidgetOptions?.inAppWebViewOptions?.toMap() ?? {});
if (Platform.isAndroid) { if (Platform.isAndroid) {
optionsMap.addAll(options.androidInAppBrowserOptions?.toMap() ?? {}); optionsMap.addAll(options.androidInAppBrowserOptions?.toMap() ?? {});
optionsMap.addAll(options.inAppWebViewWidgetOptions?.androidInAppWebViewOptions?.toMap() ?? {}); optionsMap.addAll(options
} .inAppWebViewWidgetOptions?.androidInAppWebViewOptions
else if (Platform.isIOS) { ?.toMap() ??
{});
} else if (Platform.isIOS) {
optionsMap.addAll(options.iosInAppBrowserOptions?.toMap() ?? {}); optionsMap.addAll(options.iosInAppBrowserOptions?.toMap() ?? {});
optionsMap.addAll(options.inAppWebViewWidgetOptions?.iosInAppWebViewOptions?.toMap() ?? {}); optionsMap.addAll(
options.inAppWebViewWidgetOptions?.iosInAppWebViewOptions?.toMap() ??
{});
} }
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
@ -257,19 +290,27 @@ class InAppBrowser {
args.putIfAbsent('uuid', () => uuid); args.putIfAbsent('uuid', () => uuid);
args.putIfAbsent('optionsType', () => "InAppBrowserOptions"); args.putIfAbsent('optionsType', () => "InAppBrowserOptions");
InAppBrowserClassOptions inAppBrowserClassOptions = InAppBrowserClassOptions(); InAppBrowserClassOptions inAppBrowserClassOptions =
Map<dynamic, dynamic> options = await ChannelManager.channel.invokeMethod('getOptions', args); InAppBrowserClassOptions();
Map<dynamic, dynamic> options =
await ChannelManager.channel.invokeMethod('getOptions', args);
if (options != null) { if (options != null) {
options = options.cast<String, dynamic>(); options = options.cast<String, dynamic>();
inAppBrowserClassOptions.inAppBrowserOptions = InAppBrowserOptions.fromMap(options); inAppBrowserClassOptions.inAppBrowserOptions =
inAppBrowserClassOptions.inAppWebViewWidgetOptions.inAppWebViewOptions = InAppWebViewOptions.fromMap(options); InAppBrowserOptions.fromMap(options);
inAppBrowserClassOptions.inAppWebViewWidgetOptions.inAppWebViewOptions =
InAppWebViewOptions.fromMap(options);
if (Platform.isAndroid) { if (Platform.isAndroid) {
inAppBrowserClassOptions.androidInAppBrowserOptions = AndroidInAppBrowserOptions.fromMap(options); inAppBrowserClassOptions.androidInAppBrowserOptions =
inAppBrowserClassOptions.inAppWebViewWidgetOptions.androidInAppWebViewOptions = AndroidInAppWebViewOptions.fromMap(options); AndroidInAppBrowserOptions.fromMap(options);
} inAppBrowserClassOptions
else if (Platform.isIOS) { .inAppWebViewWidgetOptions.androidInAppWebViewOptions =
inAppBrowserClassOptions.iosInAppBrowserOptions = IosInAppBrowserOptions.fromMap(options); AndroidInAppWebViewOptions.fromMap(options);
inAppBrowserClassOptions.inAppWebViewWidgetOptions.iosInAppWebViewOptions = IosInAppWebViewOptions.fromMap(options); } else if (Platform.isIOS) {
inAppBrowserClassOptions.iosInAppBrowserOptions =
IosInAppBrowserOptions.fromMap(options);
inAppBrowserClassOptions.inAppWebViewWidgetOptions
.iosInAppWebViewOptions = IosInAppWebViewOptions.fromMap(options);
} }
} }
@ -282,29 +323,19 @@ class InAppBrowser {
} }
///Event fired when the [InAppBrowser] is created. ///Event fired when the [InAppBrowser] is created.
void onBrowserCreated() { void onBrowserCreated() {}
}
///Event fired when the [InAppBrowser] window is closed. ///Event fired when the [InAppBrowser] window is closed.
void onExit() { void onExit() {}
}
///Event fired when the [InAppBrowser] starts to load an [url]. ///Event fired when the [InAppBrowser] starts to load an [url].
void onLoadStart(String url) { void onLoadStart(String url) {}
}
///Event fired when the [InAppBrowser] finishes loading an [url]. ///Event fired when the [InAppBrowser] finishes loading an [url].
void onLoadStop(String url) { void onLoadStop(String url) {}
}
///Event fired when the [InAppBrowser] encounters an error loading an [url]. ///Event fired when the [InAppBrowser] encounters an error loading an [url].
void onLoadError(String url, int code, String message) { void onLoadError(String url, int code, String message) {}
}
///Event fired when the [InAppBrowser] main page receives an HTTP error. ///Event fired when the [InAppBrowser] main page receives an HTTP error.
/// ///
@ -315,51 +346,37 @@ class InAppBrowser {
///[description] represents the description of the HTTP error. On iOS, it is always an empty string. ///[description] represents the description of the HTTP error. On iOS, it is always an empty string.
/// ///
///**NOTE**: available on Android 23+. ///**NOTE**: available on Android 23+.
void onLoadHttpError(String url, int statusCode, String description) { void onLoadHttpError(String url, int statusCode, String description) {}
}
///Event fired when the current [progress] (range 0-100) of loading a page is changed. ///Event fired when the current [progress] (range 0-100) of loading a page is changed.
void onProgressChanged(int progress) { void onProgressChanged(int progress) {}
}
///Event fired when the [InAppBrowser] webview receives a [ConsoleMessage]. ///Event fired when the [InAppBrowser] webview receives a [ConsoleMessage].
void onConsoleMessage(ConsoleMessage consoleMessage) { void onConsoleMessage(ConsoleMessage consoleMessage) {}
}
///Give the host application a chance to take control when a URL is about to be loaded in the current WebView. ///Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
/// ///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldOverrideUrlLoading] option to `true`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldOverrideUrlLoading] option to `true`.
void shouldOverrideUrlLoading(String url) { void shouldOverrideUrlLoading(String url) {}
}
///Event fired when the [InAppBrowser] webview loads a resource. ///Event fired when the [InAppBrowser] 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`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useOnLoadResource] and [InAppWebViewOptions.javaScriptEnabled] options to `true`.
void onLoadResource(LoadedResource resource) { void onLoadResource(LoadedResource resource) {}
}
///Event fired when the [InAppBrowser] webview scrolls. ///Event fired when the [InAppBrowser] webview scrolls.
/// ///
///[x] represents the current horizontal scroll origin in pixels. ///[x] represents the current horizontal scroll origin in pixels.
/// ///
///[y] represents the current vertical scroll origin in pixels. ///[y] represents the current vertical scroll origin in pixels.
void onScrollChanged(int x, int y) { void onScrollChanged(int x, int y) {}
}
///Event fired when [InAppBrowser] recognizes and starts a downloadable file. ///Event fired when [InAppBrowser] recognizes and starts a downloadable file.
/// ///
///[url] represents the url of the 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`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useOnDownloadStart] option to `true`.
void onDownloadStart(String url) { void onDownloadStart(String url) {}
}
///Event fired when the [InAppBrowser] 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`. ///Event fired when the [InAppBrowser] 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`.
/// ///
@ -367,18 +384,15 @@ class InAppBrowser {
/// ///
///[url] represents the url of the request. ///[url] represents the url of the request.
// ignore: missing_return // ignore: missing_return
Future<CustomSchemeResponse> onLoadResourceCustomScheme(String scheme, String url) { Future<CustomSchemeResponse> onLoadResourceCustomScheme(
String scheme, String url) {}
}
///Event fired when the [InAppBrowser] webview tries to open a link with `target="_blank"`. ///Event fired when the [InAppBrowser] webview tries to open a link with `target="_blank"`.
/// ///
///[url] represents the url of the link. ///[url] represents the url of the link.
/// ///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useOnTargetBlank] option to `true`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useOnTargetBlank] option to `true`.
void onTargetBlank(String url) { void onTargetBlank(String url) {}
}
///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. ///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. ///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.
@ -388,27 +402,22 @@ class InAppBrowser {
/// ///
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
// ignore: missing_return // ignore: missing_return
Future<GeolocationPermissionShowPromptResponse> onGeolocationPermissionsShowPrompt (String origin) { Future<GeolocationPermissionShowPromptResponse>
onGeolocationPermissionsShowPrompt(String origin) {}
}
///Event fired when javascript calls the `alert()` method to display an alert dialog. ///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. ///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. ///[message] represents the message to be displayed in the alert dialog.
// ignore: missing_return // ignore: missing_return
Future<JsAlertResponse> onJsAlert(String message) { Future<JsAlertResponse> onJsAlert(String message) {}
}
///Event fired when javascript calls the `confirm()` method to display a confirm dialog. ///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. ///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. ///[message] represents the message to be displayed in the alert dialog.
// ignore: missing_return // ignore: missing_return
Future<JsConfirmResponse> onJsConfirm(String message) { Future<JsConfirmResponse> onJsConfirm(String message) {}
}
///Event fired when javascript calls the `prompt()` method to display a prompt dialog. ///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. ///If [JsPromptResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog.
@ -416,9 +425,7 @@ class InAppBrowser {
///[message] represents the message to be displayed in the alert dialog. ///[message] represents the message to be displayed in the alert dialog.
///[defaultValue] represents the default value displayed in the prompt dialog. ///[defaultValue] represents the default value displayed in the prompt dialog.
// ignore: missing_return // ignore: missing_return
Future<JsPromptResponse> onJsPrompt(String message, String defaultValue) { Future<JsPromptResponse> onJsPrompt(String message, String defaultValue) {}
}
///Event fired when the WebView notifies that a loading URL has been flagged by Safe Browsing. ///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. ///The default behavior is to show an interstitial to the user, with the reporting checkbox visible.
@ -429,37 +436,33 @@ class InAppBrowser {
/// ///
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
// ignore: missing_return // ignore: missing_return
Future<SafeBrowsingResponse> onSafeBrowsingHit(String url, SafeBrowsingThreat threatType) { Future<SafeBrowsingResponse> onSafeBrowsingHit(
String url, SafeBrowsingThreat threatType) {}
}
///Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request. ///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]. ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [HttpAuthChallenge].
// ignore: missing_return // ignore: missing_return
Future<HttpAuthResponse> onReceivedHttpAuthRequest(HttpAuthChallenge challenge) { Future<HttpAuthResponse> onReceivedHttpAuthRequest(
HttpAuthChallenge challenge) {}
}
///Event fired when the WebView need to perform server trust authentication (certificate validation). ///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]. ///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]. ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ServerTrustChallenge].
// ignore: missing_return // ignore: missing_return
Future<ServerTrustAuthResponse> onReceivedServerTrustAuthRequest(ServerTrustChallenge challenge) { Future<ServerTrustAuthResponse> onReceivedServerTrustAuthRequest(
ServerTrustChallenge challenge) {}
} ///Notify the host application to handle an SSL client certificate request.
///Notify the host application to handle a SSL client certificate request.
///Webview stores the response in memory (for the life of the application) if [ClientCertResponseAction.PROCEED] or [ClientCertResponseAction.CANCEL] ///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. ///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. ///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]. ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ClientCertChallenge].
// ignore: missing_return // ignore: missing_return
Future<ClientCertResponse> onReceivedClientCertRequest(ClientCertChallenge challenge) { Future<ClientCertResponse> onReceivedClientCertRequest(
ClientCertChallenge challenge) {}
}
///Event fired as find-on-page operations progress. ///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. ///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.
@ -469,9 +472,8 @@ class InAppBrowser {
///[numberOfMatches] represents how many matches have been found. ///[numberOfMatches] represents how many matches have been found.
/// ///
///[isDoneCounting] whether the find operation has actually completed. ///[isDoneCounting] whether the find operation has actually completed.
void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, bool isDoneCounting) { void onFindResultReceived(
int activeMatchOrdinal, int numberOfMatches, bool isDoneCounting) {}
}
///Event fired when an `XMLHttpRequest` is sent to a server. ///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. ///It gives the host application a chance to take control over the request before sending it.
@ -480,9 +482,7 @@ class InAppBrowser {
/// ///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`.
// ignore: missing_return // ignore: missing_return
Future<AjaxRequest> shouldInterceptAjaxRequest(AjaxRequest ajaxRequest) { Future<AjaxRequest> shouldInterceptAjaxRequest(AjaxRequest ajaxRequest) {}
}
///Event fired whenever the `readyState` attribute of an `XMLHttpRequest` changes. ///Event fired whenever the `readyState` attribute of an `XMLHttpRequest` changes.
///It gives the host application a chance to abort the request. ///It gives the host application a chance to abort the request.
@ -491,9 +491,7 @@ class InAppBrowser {
/// ///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`.
// ignore: missing_return // ignore: missing_return
Future<AjaxRequestAction> onAjaxReadyStateChange(AjaxRequest ajaxRequest) { Future<AjaxRequestAction> onAjaxReadyStateChange(AjaxRequest ajaxRequest) {}
}
///Event fired as an `XMLHttpRequest` progress. ///Event fired as an `XMLHttpRequest` progress.
///It gives the host application a chance to abort the request. ///It gives the host application a chance to abort the request.
@ -502,9 +500,7 @@ class InAppBrowser {
/// ///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptAjaxRequest] option to `true`.
// ignore: missing_return // ignore: missing_return
Future<AjaxRequestAction> onAjaxProgress(AjaxRequest ajaxRequest) { Future<AjaxRequestAction> onAjaxProgress(AjaxRequest ajaxRequest) {}
}
///Event fired when a request is sent to a server through [Fetch API](https://developer.mozilla.org/it/docs/Web/API/Fetch_API). ///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. ///It gives the host application a chance to take control over the request before sending it.
@ -513,9 +509,7 @@ class InAppBrowser {
/// ///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptFetchRequest] option to `true`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldInterceptFetchRequest] option to `true`.
// ignore: missing_return // ignore: missing_return
Future<FetchRequest> shouldInterceptFetchRequest(FetchRequest fetchRequest) { Future<FetchRequest> shouldInterceptFetchRequest(FetchRequest fetchRequest) {}
}
///Event fired when the navigation state of the WebView changes throught the usage of ///Event fired when the navigation state of the WebView changes throught the usage of
///javascript **[History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API)** functions (`pushState()`, `replaceState()`) and `onpopstate` event. ///javascript **[History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API)** functions (`pushState()`, `replaceState()`) and `onpopstate` event.
@ -523,9 +517,7 @@ class InAppBrowser {
///Also, the event is fired when the javascript `window.location` changes without reloading the webview (for example appending or modifying an hash to the url). ///Also, the event is fired when the javascript `window.location` changes without reloading the webview (for example appending or modifying an hash to the url).
/// ///
///[url] represents the new url. ///[url] represents the new url.
void onNavigationStateChange(String url) { void onNavigationStateChange(String url) {}
}
///Event fired when the WebView is requesting permission to access the specified resources and the permission currently isn't granted or denied. ///Event fired when the WebView is requesting permission to access the specified resources and the permission currently isn't granted or denied.
/// ///
@ -534,20 +526,23 @@ class InAppBrowser {
///[resources] represents the array of resources the web content wants to access. ///[resources] represents the array of resources the web content wants to access.
/// ///
///**NOTE**: available only on Android 23+. ///**NOTE**: available only on Android 23+.
Future<PermissionRequestResponse> onPermissionRequest(String origin, List<String> resources) { // ignore: missing_return
Future<PermissionRequestResponse> onPermissionRequest(
} String origin, List<String> resources) {}
void throwIsAlreadyOpened({String message = ''}) { void throwIsAlreadyOpened({String message = ''}) {
if (this.isOpened()) { if (this.isOpened()) {
throw Exception(['Error: ${ (message.isEmpty) ? '' : message + ' '}The browser is already opened.']); throw Exception([
'Error: ${(message.isEmpty) ? '' : message + ' '}The browser is already opened.'
]);
} }
} }
void throwIsNotOpened({String message = ''}) { void throwIsNotOpened({String message = ''}) {
if (!this.isOpened()) { if (!this.isOpened()) {
throw Exception(['Error: ${ (message.isEmpty) ? '' : message + ' '}The browser is not opened.']); throw Exception([
'Error: ${(message.isEmpty) ? '' : message + ' '}The browser is not opened.'
]);
} }
} }
} }

View File

@ -8,7 +8,6 @@ import 'package:mime/mime.dart';
/// ///
///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;
int _port = 8080; int _port = 8080;
@ -28,7 +27,6 @@ class InAppLocalhostServer {
///``` ///```
///The `NSAllowsLocalNetworking` key is available since **iOS 10**. ///The `NSAllowsLocalNetworking` key is available since **iOS 10**.
Future<void> start() async { Future<void> start() async {
if (this._server != null) { if (this._server != null) {
throw Exception('Server already started on http://localhost:$_port'); throw Exception('Server already started on http://localhost:$_port');
} }
@ -48,8 +46,7 @@ class InAppLocalhostServer {
path += (path.endsWith('/')) ? 'index.html' : ''; path += (path.endsWith('/')) ? 'index.html' : '';
try { try {
body = (await rootBundle.load(path)) body = (await rootBundle.load(path)).buffer.asUint8List();
.buffer.asUint8List();
} catch (e) { } catch (e) {
print(e.toString()); print(e.toString());
request.response.close(); request.response.close();
@ -57,14 +54,17 @@ class InAppLocalhostServer {
} }
var contentType = ['text', 'html']; var contentType = ['text', 'html'];
if (!request.requestedUri.path.endsWith('/') && request.requestedUri.pathSegments.isNotEmpty) { if (!request.requestedUri.path.endsWith('/') &&
var mimeType = lookupMimeType(request.requestedUri.path, headerBytes: body); request.requestedUri.pathSegments.isNotEmpty) {
var mimeType =
lookupMimeType(request.requestedUri.path, headerBytes: body);
if (mimeType != null) { if (mimeType != null) {
contentType = mimeType.split('/'); contentType = mimeType.split('/');
} }
} }
request.response.headers.contentType = new ContentType(contentType[0], contentType[1], charset: 'utf-8'); request.response.headers.contentType =
new ContentType(contentType[0], contentType[1], charset: 'utf-8');
request.response.add(body); request.response.add(body);
request.response.close(); request.response.close();
}); });
@ -84,5 +84,4 @@ class InAppLocalhostServer {
this._server = null; this._server = null;
} }
} }
} }

View File

@ -16,24 +16,31 @@ import 'types.dart';
import 'in_app_browser.dart'; import 'in_app_browser.dart';
import 'webview_options.dart'; import 'webview_options.dart';
const javaScriptHandlerForbiddenNames = ["onLoadResource", "shouldInterceptAjaxRequest", "onAjaxReadyStateChange", "onAjaxProgress", "shouldInterceptFetchRequest"]; const javaScriptHandlerForbiddenNames = [
"onLoadResource",
"shouldInterceptAjaxRequest",
"onAjaxReadyStateChange",
"onAjaxProgress",
"shouldInterceptFetchRequest"
];
///InAppWebView Widget class. ///InAppWebView Widget class.
/// ///
///Flutter Widget for adding an **inline native WebView** integrated in the flutter widget tree. ///Flutter Widget for adding an **inline native WebView** integrated in the flutter widget tree.
class InAppWebView extends StatefulWidget { class InAppWebView extends StatefulWidget {
///Event fired when the [InAppWebView] is created. ///Event fired when the [InAppWebView] is created.
final void Function(InAppWebViewController controller) onWebViewCreated; final void Function(InAppWebViewController controller) onWebViewCreated;
///Event fired when the [InAppWebView] starts to load an [url]. ///Event fired when the [InAppWebView] starts to load an [url].
final void Function(InAppWebViewController controller, String url) onLoadStart; final void Function(InAppWebViewController controller, String url)
onLoadStart;
///Event fired when the [InAppWebView] finishes loading an [url]. ///Event fired when the [InAppWebView] finishes loading an [url].
final void Function(InAppWebViewController controller, String url) onLoadStop; final void Function(InAppWebViewController controller, String url) onLoadStop;
///Event fired when the [InAppWebView] encounters an error loading an [url]. ///Event fired when the [InAppWebView] encounters an error loading an [url].
final void Function(InAppWebViewController controller, String url, int code, String message) onLoadError; final void Function(InAppWebViewController controller, String url, int code,
String message) onLoadError;
///Event fired when the [InAppWebView] main page receives an HTTP error. ///Event fired when the [InAppWebView] main page receives an HTTP error.
/// ///
@ -44,51 +51,63 @@ class InAppWebView extends StatefulWidget {
///[description] represents the description of the HTTP error. On iOS, it is always an empty string. ///[description] represents the description of the HTTP error. On iOS, it is always an empty string.
/// ///
///**NOTE**: available on Android 23+. ///**NOTE**: available on Android 23+.
final void Function(InAppWebViewController controller, String url, int statusCode, String description) onLoadHttpError; final void Function(InAppWebViewController controller, String url,
int statusCode, String description) onLoadHttpError;
///Event fired when the current [progress] of loading a page is changed. ///Event fired when the current [progress] of loading a page is changed.
final void Function(InAppWebViewController controller, int progress) onProgressChanged; final void Function(InAppWebViewController controller, int progress)
onProgressChanged;
///Event fired when the [InAppWebView] receives a [ConsoleMessage]. ///Event fired when the [InAppWebView] receives a [ConsoleMessage].
final void Function(InAppWebViewController controller, ConsoleMessage consoleMessage) onConsoleMessage; 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. ///Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
/// ///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldOverrideUrlLoading] option to `true`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldOverrideUrlLoading] option to `true`.
final void Function(InAppWebViewController controller, String url) shouldOverrideUrlLoading; final void Function(InAppWebViewController controller, String url)
shouldOverrideUrlLoading;
///Event fired when the [InAppWebView] loads a resource. ///Event fired when the [InAppWebView] 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`. ///**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; final void Function(
InAppWebViewController controller, LoadedResource resource)
onLoadResource;
///Event fired when the [InAppWebView] scrolls. ///Event fired when the [InAppWebView] scrolls.
/// ///
///[x] represents the current horizontal scroll origin in pixels. ///[x] represents the current horizontal scroll origin in pixels.
/// ///
///[y] represents the current vertical scroll origin in pixels. ///[y] represents the current vertical scroll origin in pixels.
final void Function(InAppWebViewController controller, int x, int y) onScrollChanged; final void Function(InAppWebViewController controller, int x, int y)
onScrollChanged;
///Event fired when [InAppWebView] recognizes and starts a downloadable file. ///Event fired when [InAppWebView] recognizes and starts a downloadable file.
/// ///
///[url] represents the url of the 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`. ///**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; final void Function(InAppWebViewController controller, String url)
onDownloadStart;
///Event fired when the [InAppWebView] 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`. ///Event fired when the [InAppWebView] 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. ///[scheme] represents the scheme of the url.
/// ///
///[url] represents the url of the request. ///[url] represents the url of the request.
final Future<CustomSchemeResponse> Function(InAppWebViewController controller, String scheme, String url) onLoadResourceCustomScheme; final Future<CustomSchemeResponse> Function(
InAppWebViewController controller, String scheme, String url)
onLoadResourceCustomScheme;
///Event fired when the [InAppWebView] tries to open a link with `target="_blank"`. ///Event fired when the [InAppWebView] tries to open a link with `target="_blank"`.
/// ///
///[url] represents the url of the link. ///[url] represents the url of the link.
/// ///
///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useOnTargetBlank] option to `true`. ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useOnTargetBlank] option to `true`.
final void Function(InAppWebViewController controller, String url) onTargetBlank; final void Function(InAppWebViewController controller, String url)
onTargetBlank;
///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. ///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. ///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.
@ -97,19 +116,23 @@ class InAppWebView extends StatefulWidget {
///[origin] represents the origin of the web content attempting to use the Geolocation API. ///[origin] represents the origin of the web content attempting to use the Geolocation API.
/// ///
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
final Future<GeolocationPermissionShowPromptResponse> Function(InAppWebViewController controller, String origin) onGeolocationPermissionsShowPrompt; final Future<GeolocationPermissionShowPromptResponse> Function(
InAppWebViewController controller, String origin)
onGeolocationPermissionsShowPrompt;
///Event fired when javascript calls the `alert()` method to display an alert dialog. ///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. ///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. ///[message] represents the message to be displayed in the alert dialog.
final Future<JsAlertResponse> Function(InAppWebViewController controller, String message) onJsAlert; final Future<JsAlertResponse> Function(
InAppWebViewController controller, String message) onJsAlert;
///Event fired when javascript calls the `confirm()` method to display a confirm dialog. ///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. ///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. ///[message] represents the message to be displayed in the alert dialog.
final Future<JsConfirmResponse> Function(InAppWebViewController controller, String message) onJsConfirm; final Future<JsConfirmResponse> Function(
InAppWebViewController controller, String message) onJsConfirm;
///Event fired when javascript calls the `prompt()` method to display a prompt dialog. ///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. ///If [JsPromptResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog.
@ -117,7 +140,8 @@ class InAppWebView extends StatefulWidget {
///[message] represents the message to be displayed in the alert dialog. ///[message] represents the message to be displayed in the alert dialog.
/// ///
///[defaultValue] represents the default value displayed in the prompt dialog. ///[defaultValue] represents the default value displayed in the prompt dialog.
final Future<JsPromptResponse> Function(InAppWebViewController controller, String message, String defaultValue) onJsPrompt; final Future<JsPromptResponse> Function(InAppWebViewController controller,
String message, String defaultValue) onJsPrompt;
///Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing. ///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. ///The default behavior is to show an interstitial to the user, with the reporting checkbox visible.
@ -127,26 +151,33 @@ class InAppWebView extends StatefulWidget {
///[threatType] represents the reason the resource was caught by Safe Browsing, corresponding to a [SafeBrowsingThreat]. ///[threatType] represents the reason the resource was caught by Safe Browsing, corresponding to a [SafeBrowsingThreat].
/// ///
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
final Future<SafeBrowsingResponse> Function(InAppWebViewController controller, String url, SafeBrowsingThreat threatType) onSafeBrowsingHit; final Future<SafeBrowsingResponse> Function(InAppWebViewController controller,
String url, SafeBrowsingThreat threatType) onSafeBrowsingHit;
///Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request. ///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]. ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [HttpAuthChallenge].
final Future<HttpAuthResponse> Function(InAppWebViewController controller, HttpAuthChallenge challenge) onReceivedHttpAuthRequest; final Future<HttpAuthResponse> Function(
InAppWebViewController controller, HttpAuthChallenge challenge)
onReceivedHttpAuthRequest;
///Event fired when the WebView need to perform server trust authentication (certificate validation). ///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]. ///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]. ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ServerTrustChallenge].
final Future<ServerTrustAuthResponse> Function(InAppWebViewController controller, ServerTrustChallenge challenge) onReceivedServerTrustAuthRequest; final Future<ServerTrustAuthResponse> Function(
InAppWebViewController controller, ServerTrustChallenge challenge)
onReceivedServerTrustAuthRequest;
///Notify the host application to handle a SSL client certificate request. ///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] ///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. ///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. ///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]. ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ClientCertChallenge].
final Future<ClientCertResponse> Function(InAppWebViewController controller, ClientCertChallenge challenge) onReceivedClientCertRequest; final Future<ClientCertResponse> Function(
InAppWebViewController controller, ClientCertChallenge challenge)
onReceivedClientCertRequest;
///Event fired as find-on-page operations progress. ///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. ///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.
@ -156,7 +187,8 @@ class InAppWebView extends StatefulWidget {
///[numberOfMatches] represents how many matches have been found. ///[numberOfMatches] represents how many matches have been found.
/// ///
///[isDoneCounting] whether the find operation has actually completed. ///[isDoneCounting] whether the find operation has actually completed.
final void Function(InAppWebViewController controller, int activeMatchOrdinal, int numberOfMatches, bool isDoneCounting) onFindResultReceived; final void Function(InAppWebViewController controller, int activeMatchOrdinal,
int numberOfMatches, bool isDoneCounting) onFindResultReceived;
///Event fired when an `XMLHttpRequest` is sent to a server. ///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. ///It gives the host application a chance to take control over the request before sending it.
@ -168,7 +200,9 @@ class InAppWebView extends StatefulWidget {
///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code ///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). ///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. ///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure.
final Future<AjaxRequest> Function(InAppWebViewController controller, AjaxRequest ajaxRequest) shouldInterceptAjaxRequest; final Future<AjaxRequest> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)
shouldInterceptAjaxRequest;
///Event fired whenever the `readyState` attribute of an `XMLHttpRequest` changes. ///Event fired whenever the `readyState` attribute of an `XMLHttpRequest` changes.
///It gives the host application a chance to abort the request. ///It gives the host application a chance to abort the request.
@ -180,7 +214,9 @@ class InAppWebView extends StatefulWidget {
///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code ///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). ///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. ///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure.
final Future<AjaxRequestAction> Function(InAppWebViewController controller, AjaxRequest ajaxRequest) onAjaxReadyStateChange; final Future<AjaxRequestAction> Function(
InAppWebViewController controller, AjaxRequest ajaxRequest)
onAjaxReadyStateChange;
///Event fired as an `XMLHttpRequest` progress. ///Event fired as an `XMLHttpRequest` progress.
///It gives the host application a chance to abort the request. ///It gives the host application a chance to abort the request.
@ -192,7 +228,9 @@ class InAppWebView extends StatefulWidget {
///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code ///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). ///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. ///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the ajax requests will be intercept for sure.
final Future<AjaxRequestAction> Function(InAppWebViewController controller, AjaxRequest ajaxRequest) onAjaxProgress; 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). ///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. ///It gives the host application a chance to take control over the request before sending it.
@ -204,7 +242,9 @@ class InAppWebView extends StatefulWidget {
///can inject javascript code right after the document element is created but before any other content is loaded, in Android the javascript code ///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). ///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. ///Inside the `window.addEventListener("flutterInAppWebViewPlatformReady")` event, the fetch requests will be intercept for sure.
final Future<FetchRequest> Function(InAppWebViewController controller, FetchRequest fetchRequest) shouldInterceptFetchRequest; final Future<FetchRequest> Function(
InAppWebViewController controller, FetchRequest fetchRequest)
shouldInterceptFetchRequest;
///Event fired when the navigation state of the [InAppWebView] changes throught the usage of ///Event fired when the navigation state of the [InAppWebView] changes throught the usage of
///javascript **[History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API)** functions (`pushState()`, `replaceState()`) and `onpopstate` event. ///javascript **[History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API)** functions (`pushState()`, `replaceState()`) and `onpopstate` event.
@ -212,7 +252,8 @@ class InAppWebView extends StatefulWidget {
///Also, the event is fired when the javascript `window.location` changes without reloading the webview (for example appending or modifying an hash to the url). ///Also, the event is fired when the javascript `window.location` changes without reloading the webview (for example appending or modifying an hash to the url).
/// ///
///[url] represents the new url. ///[url] represents the new url.
final void Function(InAppWebViewController controller, String url) onNavigationStateChange; final void Function(InAppWebViewController controller, String url)
onNavigationStateChange;
///Event fired when the WebView is requesting permission to access the specified resources and the permission currently isn't granted or denied. ///Event fired when the WebView is requesting permission to access the specified resources and the permission currently isn't granted or denied.
/// ///
@ -221,18 +262,26 @@ class InAppWebView extends StatefulWidget {
///[resources] represents the array of resources the web content wants to access. ///[resources] represents the array of resources the web content wants to access.
/// ///
///**NOTE**: available only on Android 23+. ///**NOTE**: available only on Android 23+.
final Future<PermissionRequestResponse> Function(InAppWebViewController controller, String origin, List<String> resources) onPermissionRequest; final Future<PermissionRequestResponse> Function(
InAppWebViewController controller,
String origin,
List<String> resources) onPermissionRequest;
///Initial url that will be loaded. ///Initial url that will be loaded.
final String initialUrl; final String initialUrl;
///Initial asset file that will be loaded. See [InAppWebView.loadFile()] for explanation. ///Initial asset file that will be loaded. See [InAppWebView.loadFile()] for explanation.
final String initialFile; final String initialFile;
///Initial [InAppWebViewInitialData] that will be loaded. ///Initial [InAppWebViewInitialData] that will be loaded.
final InAppWebViewInitialData initialData; final InAppWebViewInitialData initialData;
///Initial headers that will be used. ///Initial headers that will be used.
final Map<String, String> initialHeaders; final Map<String, String> initialHeaders;
///Initial options that will be used. ///Initial options that will be used.
final InAppWebViewWidgetOptions initialOptions; final InAppWebViewWidgetOptions initialOptions;
/// `gestureRecognizers` specifies which gestures should be consumed by the web view. /// `gestureRecognizers` specifies which gestures should be consumed by the web view.
/// It is possible for other gesture recognizers to be competing with the web view on pointer /// It is possible for other gesture recognizers to be competing with the web view on pointer
/// events, e.g if the web view is inside a [ListView] the [ListView] will want to handle /// events, e.g if the web view is inside a [ListView] the [ListView] will want to handle
@ -248,7 +297,7 @@ class InAppWebView extends StatefulWidget {
this.initialFile, this.initialFile,
this.initialData, this.initialData,
this.initialHeaders = const {}, this.initialHeaders = const {},
this.initialOptions, @required this.initialOptions,
this.onWebViewCreated, this.onWebViewCreated,
this.onLoadStart, this.onLoadStart,
this.onLoadStop, this.onLoadStop,
@ -285,17 +334,19 @@ class InAppWebView extends StatefulWidget {
} }
class _InAppWebViewState extends State<InAppWebView> { class _InAppWebViewState extends State<InAppWebView> {
InAppWebViewController _controller; InAppWebViewController _controller;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Map<String, dynamic> initialOptions = {}; Map<String, dynamic> initialOptions = {};
initialOptions.addAll(widget.initialOptions.inAppWebViewOptions?.toMap() ?? {}); initialOptions
.addAll(widget.initialOptions.inAppWebViewOptions?.toMap() ?? {});
if (Platform.isAndroid) if (Platform.isAndroid)
initialOptions.addAll(widget.initialOptions.androidInAppWebViewOptions?.toMap() ?? {}); initialOptions.addAll(
widget.initialOptions.androidInAppWebViewOptions?.toMap() ?? {});
else if (Platform.isIOS) else if (Platform.isIOS)
initialOptions.addAll(widget.initialOptions.iosInAppWebViewOptions?.toMap() ?? {}); initialOptions
.addAll(widget.initialOptions.iosInAppWebViewOptions?.toMap() ?? {});
if (defaultTargetPlatform == TargetPlatform.android) { if (defaultTargetPlatform == TargetPlatform.android) {
return AndroidView( return AndroidView(
@ -356,7 +407,7 @@ class _InAppWebViewState extends State<InAppWebView> {
} }
@override @override
void dispose(){ void dispose() {
super.dispose(); super.dispose();
} }
@ -373,10 +424,10 @@ class _InAppWebViewState extends State<InAppWebView> {
/// An [InAppWebViewController] instance can be obtained by setting the [InAppWebView.onWebViewCreated] /// An [InAppWebViewController] instance can be obtained by setting the [InAppWebView.onWebViewCreated]
/// callback for an [InAppWebView] widget. /// callback for an [InAppWebView] widget.
class InAppWebViewController { class InAppWebViewController {
InAppWebView _widget; InAppWebView _widget;
MethodChannel _channel; MethodChannel _channel;
Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap = HashMap<String, JavaScriptHandlerCallback>(); Map<String, JavaScriptHandlerCallback> javaScriptHandlersMap =
HashMap<String, JavaScriptHandlerCallback>();
// ignore: unused_field // ignore: unused_field
bool _isOpened = false; bool _isOpened = false;
// ignore: unused_field // ignore: unused_field
@ -384,35 +435,34 @@ class InAppWebViewController {
String _inAppBrowserUuid; String _inAppBrowserUuid;
InAppBrowser _inAppBrowser; InAppBrowser _inAppBrowser;
InAppWebViewController(int id, InAppWebView widget) { InAppWebViewController(int id, InAppWebView widget) {
this._id = id; this._id = id;
this._channel = MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id'); this._channel =
MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id');
this._channel.setMethodCallHandler(handleMethod); this._channel.setMethodCallHandler(handleMethod);
this._widget = widget; this._widget = widget;
} }
InAppWebViewController.fromInAppBrowser(String uuid, MethodChannel channel, InAppBrowser inAppBrowser) { InAppWebViewController.fromInAppBrowser(
String uuid, MethodChannel channel, InAppBrowser inAppBrowser) {
this._inAppBrowserUuid = uuid; this._inAppBrowserUuid = uuid;
this._channel = channel; this._channel = channel;
this._inAppBrowser = inAppBrowser; this._inAppBrowser = inAppBrowser;
} }
Future<dynamic> handleMethod(MethodCall call) async { Future<dynamic> handleMethod(MethodCall call) async {
switch(call.method) { switch (call.method) {
case "onLoadStart": case "onLoadStart":
String url = call.arguments["url"]; String url = call.arguments["url"];
if (_widget != null && _widget.onLoadStart != null) if (_widget != null && _widget.onLoadStart != null)
_widget.onLoadStart(this, url); _widget.onLoadStart(this, url);
else if (_inAppBrowser != null) else if (_inAppBrowser != null) _inAppBrowser.onLoadStart(url);
_inAppBrowser.onLoadStart(url);
break; break;
case "onLoadStop": case "onLoadStop":
String url = call.arguments["url"]; String url = call.arguments["url"];
if (_widget != null && _widget.onLoadStop != null) if (_widget != null && _widget.onLoadStop != null)
_widget.onLoadStop(this, url); _widget.onLoadStop(this, url);
else if (_inAppBrowser != null) else if (_inAppBrowser != null) _inAppBrowser.onLoadStop(url);
_inAppBrowser.onLoadStop(url);
break; break;
case "onLoadError": case "onLoadError":
String url = call.arguments["url"]; String url = call.arguments["url"];
@ -448,8 +498,10 @@ class InAppWebViewController {
break; break;
case "onConsoleMessage": case "onConsoleMessage":
String message = call.arguments["message"]; String message = call.arguments["message"];
ConsoleMessageLevel messageLevel = ConsoleMessageLevel.fromValue(call.arguments["messageLevel"]); ConsoleMessageLevel messageLevel =
ConsoleMessage consoleMessage = ConsoleMessage(message: message, messageLevel: messageLevel); ConsoleMessageLevel.fromValue(call.arguments["messageLevel"]);
ConsoleMessage consoleMessage =
ConsoleMessage(message: message, messageLevel: messageLevel);
if (_widget != null && _widget.onConsoleMessage != null) if (_widget != null && _widget.onConsoleMessage != null)
_widget.onConsoleMessage(this, consoleMessage); _widget.onConsoleMessage(this, consoleMessage);
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
@ -460,31 +512,31 @@ class InAppWebViewController {
int y = call.arguments["y"]; int y = call.arguments["y"];
if (_widget != null && _widget.onScrollChanged != null) if (_widget != null && _widget.onScrollChanged != null)
_widget.onScrollChanged(this, x, y); _widget.onScrollChanged(this, x, y);
else if (_inAppBrowser != null) else if (_inAppBrowser != null) _inAppBrowser.onScrollChanged(x, y);
_inAppBrowser.onScrollChanged(x, y);
break; break;
case "onDownloadStart": case "onDownloadStart":
String url = call.arguments["url"]; String url = call.arguments["url"];
if (_widget != null && _widget.onDownloadStart != null) if (_widget != null && _widget.onDownloadStart != null)
_widget.onDownloadStart(this, url); _widget.onDownloadStart(this, url);
else if (_inAppBrowser != null) else if (_inAppBrowser != null) _inAppBrowser.onDownloadStart(url);
_inAppBrowser.onDownloadStart(url);
break; break;
case "onLoadResourceCustomScheme": case "onLoadResourceCustomScheme":
String scheme = call.arguments["scheme"]; String scheme = call.arguments["scheme"];
String url = call.arguments["url"]; String url = call.arguments["url"];
if (_widget != null && _widget.onLoadResourceCustomScheme != null) { if (_widget != null && _widget.onLoadResourceCustomScheme != null) {
try { try {
var response = await _widget.onLoadResourceCustomScheme(this, scheme, url); var response =
return (response != null) ? response.toJson(): null; await _widget.onLoadResourceCustomScheme(this, scheme, url);
return (response != null) ? response.toJson() : null;
} catch (error) { } catch (error) {
print(error); print(error);
return null; return null;
} }
} else if (_inAppBrowser != null) { } else if (_inAppBrowser != null) {
try { try {
var response = await _inAppBrowser.onLoadResourceCustomScheme(scheme, url); var response =
return (response != null) ? response.toJson(): null; await _inAppBrowser.onLoadResourceCustomScheme(scheme, url);
return (response != null) ? response.toJson() : null;
} catch (error) { } catch (error) {
print(error); print(error);
return null; return null;
@ -495,15 +547,19 @@ class InAppWebViewController {
String url = call.arguments["url"]; String url = call.arguments["url"];
if (_widget != null && _widget.onTargetBlank != null) if (_widget != null && _widget.onTargetBlank != null)
_widget.onTargetBlank(this, url); _widget.onTargetBlank(this, url);
else if (_inAppBrowser != null) else if (_inAppBrowser != null) _inAppBrowser.onTargetBlank(url);
_inAppBrowser.onTargetBlank(url);
break; break;
case "onGeolocationPermissionsShowPrompt": case "onGeolocationPermissionsShowPrompt":
String origin = call.arguments["origin"]; String origin = call.arguments["origin"];
if (_widget != null && _widget.onGeolocationPermissionsShowPrompt != null) if (_widget != null &&
return (await _widget.onGeolocationPermissionsShowPrompt(this, origin))?.toMap(); _widget.onGeolocationPermissionsShowPrompt != null)
return (await _widget.onGeolocationPermissionsShowPrompt(
this, origin))
?.toMap();
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
return (await _inAppBrowser.onGeolocationPermissionsShowPrompt(origin))?.toMap(); return (await _inAppBrowser
.onGeolocationPermissionsShowPrompt(origin))
?.toMap();
break; break;
case "onJsAlert": case "onJsAlert":
String message = call.arguments["message"]; String message = call.arguments["message"];
@ -523,17 +579,22 @@ class InAppWebViewController {
String message = call.arguments["message"]; String message = call.arguments["message"];
String defaultValue = call.arguments["defaultValue"]; String defaultValue = call.arguments["defaultValue"];
if (_widget != null && _widget.onJsPrompt != null) if (_widget != null && _widget.onJsPrompt != null)
return (await _widget.onJsPrompt(this, message, defaultValue))?.toMap(); return (await _widget.onJsPrompt(this, message, defaultValue))
?.toMap();
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
return (await _inAppBrowser.onJsPrompt(message, defaultValue))?.toMap(); return (await _inAppBrowser.onJsPrompt(message, defaultValue))
?.toMap();
break; break;
case "onSafeBrowsingHit": case "onSafeBrowsingHit":
String url = call.arguments["url"]; String url = call.arguments["url"];
SafeBrowsingThreat threatType = SafeBrowsingThreat.fromValue(call.arguments["threatType"]); SafeBrowsingThreat threatType =
SafeBrowsingThreat.fromValue(call.arguments["threatType"]);
if (_widget != null && _widget.onSafeBrowsingHit != null) if (_widget != null && _widget.onSafeBrowsingHit != null)
return (await _widget.onSafeBrowsingHit(this, url, threatType))?.toMap(); return (await _widget.onSafeBrowsingHit(this, url, threatType))
?.toMap();
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
return (await _inAppBrowser.onSafeBrowsingHit(url, threatType))?.toMap(); return (await _inAppBrowser.onSafeBrowsingHit(url, threatType))
?.toMap();
break; break;
case "onReceivedHttpAuthRequest": case "onReceivedHttpAuthRequest":
String host = call.arguments["host"]; String host = call.arguments["host"];
@ -541,12 +602,17 @@ class InAppWebViewController {
String realm = call.arguments["realm"]; String realm = call.arguments["realm"];
int port = call.arguments["port"]; int port = call.arguments["port"];
int previousFailureCount = call.arguments["previousFailureCount"]; int previousFailureCount = call.arguments["previousFailureCount"];
var protectionSpace = ProtectionSpace(host: host, protocol: protocol, realm: realm, port: port); var protectionSpace = ProtectionSpace(
var challenge = HttpAuthChallenge(previousFailureCount: previousFailureCount, protectionSpace: protectionSpace); host: host, protocol: protocol, realm: realm, port: port);
var challenge = HttpAuthChallenge(
previousFailureCount: previousFailureCount,
protectionSpace: protectionSpace);
if (_widget != null && _widget.onReceivedHttpAuthRequest != null) if (_widget != null && _widget.onReceivedHttpAuthRequest != null)
return (await _widget.onReceivedHttpAuthRequest(this, challenge))?.toMap(); return (await _widget.onReceivedHttpAuthRequest(this, challenge))
?.toMap();
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
return (await _inAppBrowser.onReceivedHttpAuthRequest(challenge))?.toMap(); return (await _inAppBrowser.onReceivedHttpAuthRequest(challenge))
?.toMap();
break; break;
case "onReceivedServerTrustAuthRequest": case "onReceivedServerTrustAuthRequest":
String host = call.arguments["host"]; String host = call.arguments["host"];
@ -556,33 +622,47 @@ class InAppWebViewController {
int error = call.arguments["error"]; int error = call.arguments["error"];
String message = call.arguments["message"]; String message = call.arguments["message"];
Uint8List serverCertificate = call.arguments["serverCertificate"]; Uint8List serverCertificate = call.arguments["serverCertificate"];
var protectionSpace = ProtectionSpace(host: host, protocol: protocol, realm: realm, port: port); var protectionSpace = ProtectionSpace(
var challenge = ServerTrustChallenge(protectionSpace: protectionSpace, error: error, message: message, serverCertificate: serverCertificate); host: host, protocol: protocol, realm: realm, port: port);
var challenge = ServerTrustChallenge(
protectionSpace: protectionSpace,
error: error,
message: message,
serverCertificate: serverCertificate);
if (_widget != null && _widget.onReceivedServerTrustAuthRequest != null) if (_widget != null && _widget.onReceivedServerTrustAuthRequest != null)
return (await _widget.onReceivedServerTrustAuthRequest(this, challenge))?.toMap(); return (await _widget.onReceivedServerTrustAuthRequest(
this, challenge))
?.toMap();
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
return (await _inAppBrowser.onReceivedServerTrustAuthRequest(challenge))?.toMap(); return (await _inAppBrowser
.onReceivedServerTrustAuthRequest(challenge))
?.toMap();
break; break;
case "onReceivedClientCertRequest": case "onReceivedClientCertRequest":
String host = call.arguments["host"]; String host = call.arguments["host"];
String protocol = call.arguments["protocol"]; String protocol = call.arguments["protocol"];
String realm = call.arguments["realm"]; String realm = call.arguments["realm"];
int port = call.arguments["port"]; int port = call.arguments["port"];
var protectionSpace = ProtectionSpace(host: host, protocol: protocol, realm: realm, port: port); var protectionSpace = ProtectionSpace(
host: host, protocol: protocol, realm: realm, port: port);
var challenge = ClientCertChallenge(protectionSpace: protectionSpace); var challenge = ClientCertChallenge(protectionSpace: protectionSpace);
if (_widget != null && _widget.onReceivedClientCertRequest != null) if (_widget != null && _widget.onReceivedClientCertRequest != null)
return (await _widget.onReceivedClientCertRequest(this, challenge))?.toMap(); return (await _widget.onReceivedClientCertRequest(this, challenge))
?.toMap();
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
return (await _inAppBrowser.onReceivedClientCertRequest(challenge))?.toMap(); return (await _inAppBrowser.onReceivedClientCertRequest(challenge))
?.toMap();
break; break;
case "onFindResultReceived": case "onFindResultReceived":
int activeMatchOrdinal = call.arguments["activeMatchOrdinal"]; int activeMatchOrdinal = call.arguments["activeMatchOrdinal"];
int numberOfMatches = call.arguments["numberOfMatches"]; int numberOfMatches = call.arguments["numberOfMatches"];
bool isDoneCounting = call.arguments["isDoneCounting"]; bool isDoneCounting = call.arguments["isDoneCounting"];
if (_widget != null && _widget.onFindResultReceived != null) if (_widget != null && _widget.onFindResultReceived != null)
_widget.onFindResultReceived(this, activeMatchOrdinal, numberOfMatches, isDoneCounting); _widget.onFindResultReceived(
this, activeMatchOrdinal, numberOfMatches, isDoneCounting);
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
_inAppBrowser.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting); _inAppBrowser.onFindResultReceived(
activeMatchOrdinal, numberOfMatches, isDoneCounting);
break; break;
case "onNavigationStateChange": case "onNavigationStateChange":
String url = call.arguments["url"]; String url = call.arguments["url"];
@ -595,24 +675,34 @@ class InAppWebViewController {
String origin = call.arguments["origin"]; String origin = call.arguments["origin"];
List<String> resources = call.arguments["resources"].cast<String>(); List<String> resources = call.arguments["resources"].cast<String>();
if (_widget != null && _widget.onPermissionRequest != null) if (_widget != null && _widget.onPermissionRequest != null)
return (await _widget.onPermissionRequest(this, origin, resources))?.toMap(); return (await _widget.onPermissionRequest(this, origin, resources))
?.toMap();
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
return (await _inAppBrowser.onPermissionRequest(origin, resources))?.toMap(); return (await _inAppBrowser.onPermissionRequest(origin, resources))
?.toMap();
break; break;
case "onCallJsHandler": case "onCallJsHandler":
String handlerName = call.arguments["handlerName"]; String handlerName = call.arguments["handlerName"];
// decode args to json // decode args to json
List<dynamic> args = jsonDecode(call.arguments["args"]); List<dynamic> args = jsonDecode(call.arguments["args"]);
switch(handlerName) { switch (handlerName) {
case "onLoadResource": case "onLoadResource":
Map<dynamic, dynamic> argMap = args[0]; Map<dynamic, dynamic> argMap = args[0];
String initiatorType = argMap["initiatorType"]; String initiatorType = argMap["initiatorType"];
String url = argMap["name"]; String url = argMap["name"];
double startTime = argMap["startTime"] is int ? argMap["startTime"].toDouble() : argMap["startTime"]; double startTime = argMap["startTime"] is int
double duration = argMap["duration"] is int ? argMap["duration"].toDouble() : argMap["duration"]; ? argMap["startTime"].toDouble()
: argMap["startTime"];
double duration = argMap["duration"] is int
? argMap["duration"].toDouble()
: argMap["duration"];
var response = new LoadedResource(initiatorType: initiatorType, url: url, startTime: startTime, duration: duration); var response = new LoadedResource(
initiatorType: initiatorType,
url: url,
startTime: startTime,
duration: duration);
if (_widget != null && _widget.onLoadResource != null) if (_widget != null && _widget.onLoadResource != null)
_widget.onLoadResource(this, response); _widget.onLoadResource(this, response);
@ -631,12 +721,23 @@ class InAppWebViewController {
AjaxRequestHeaders headers = AjaxRequestHeaders(argMap["headers"]); AjaxRequestHeaders headers = AjaxRequestHeaders(argMap["headers"]);
String responseType = argMap["responseType"]; String responseType = argMap["responseType"];
var request = new AjaxRequest(data: data, method: method, url: url, isAsync: isAsync, user: user, password: password, withCredentials: withCredentials, headers: headers, responseType: responseType); var request = new AjaxRequest(
data: data,
method: method,
url: url,
isAsync: isAsync,
user: user,
password: password,
withCredentials: withCredentials,
headers: headers,
responseType: responseType);
if (_widget != null && _widget.shouldInterceptAjaxRequest != null) if (_widget != null && _widget.shouldInterceptAjaxRequest != null)
return jsonEncode(await _widget.shouldInterceptAjaxRequest(this, request)); return jsonEncode(
await _widget.shouldInterceptAjaxRequest(this, request));
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
return jsonEncode(await _inAppBrowser.shouldInterceptAjaxRequest(request)); return jsonEncode(
await _inAppBrowser.shouldInterceptAjaxRequest(request));
return null; return null;
case "onAjaxReadyStateChange": case "onAjaxReadyStateChange":
Map<dynamic, dynamic> argMap = args[0]; Map<dynamic, dynamic> argMap = args[0];
@ -658,14 +759,31 @@ class InAppWebViewController {
String statusText = argMap["statusText"]; String statusText = argMap["statusText"];
Map<dynamic, dynamic> responseHeaders = argMap["responseHeaders"]; Map<dynamic, dynamic> responseHeaders = argMap["responseHeaders"];
var request = new AjaxRequest(data: data, method: method, url: url, isAsync: isAsync, user: user, password: password, var request = new AjaxRequest(
withCredentials: withCredentials, headers: headers, readyState: AjaxRequestReadyState.fromValue(readyState), status: status, responseURL: responseURL, data: data,
responseType: responseType, response: response, responseText: responseText, responseXML: responseXML, statusText: statusText, responseHeaders: responseHeaders); method: method,
url: url,
isAsync: isAsync,
user: user,
password: password,
withCredentials: withCredentials,
headers: headers,
readyState: AjaxRequestReadyState.fromValue(readyState),
status: status,
responseURL: responseURL,
responseType: responseType,
response: response,
responseText: responseText,
responseXML: responseXML,
statusText: statusText,
responseHeaders: responseHeaders);
if (_widget != null && _widget.onAjaxReadyStateChange != null) if (_widget != null && _widget.onAjaxReadyStateChange != null)
return jsonEncode(await _widget.onAjaxReadyStateChange(this, request)); return jsonEncode(
await _widget.onAjaxReadyStateChange(this, request));
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
return jsonEncode(await _inAppBrowser.onAjaxReadyStateChange(request)); return jsonEncode(
await _inAppBrowser.onAjaxReadyStateChange(request));
return null; return null;
case "onAjaxProgress": case "onAjaxProgress":
Map<dynamic, dynamic> argMap = args[0]; Map<dynamic, dynamic> argMap = args[0];
@ -688,11 +806,31 @@ class InAppWebViewController {
Map<dynamic, dynamic> responseHeaders = argMap["responseHeaders"]; Map<dynamic, dynamic> responseHeaders = argMap["responseHeaders"];
Map<dynamic, dynamic> eventMap = argMap["event"]; Map<dynamic, dynamic> eventMap = argMap["event"];
AjaxRequestEvent event = AjaxRequestEvent(lengthComputable: eventMap["lengthComputable"], loaded: eventMap["loaded"], total: eventMap["total"], type: AjaxRequestEventType.fromValue(eventMap["type"])); AjaxRequestEvent event = AjaxRequestEvent(
lengthComputable: eventMap["lengthComputable"],
loaded: eventMap["loaded"],
total: eventMap["total"],
type: AjaxRequestEventType.fromValue(eventMap["type"]));
var request = new AjaxRequest(data: data, method: method, url: url, isAsync: isAsync, user: user, password: password, var request = new AjaxRequest(
withCredentials: withCredentials, headers: headers, readyState: AjaxRequestReadyState.fromValue(readyState), status: status, responseURL: responseURL, data: data,
responseType: responseType, response: response, responseText: responseText, responseXML: responseXML, statusText: statusText, responseHeaders: responseHeaders, event: event); method: method,
url: url,
isAsync: isAsync,
user: user,
password: password,
withCredentials: withCredentials,
headers: headers,
readyState: AjaxRequestReadyState.fromValue(readyState),
status: status,
responseURL: responseURL,
responseType: responseType,
response: response,
responseText: responseText,
responseXML: responseXML,
statusText: statusText,
responseHeaders: responseHeaders,
event: event);
if (_widget != null && _widget.onAjaxProgress != null) if (_widget != null && _widget.onAjaxProgress != null)
return jsonEncode(await _widget.onAjaxProgress(this, request)); return jsonEncode(await _widget.onAjaxProgress(this, request));
@ -706,7 +844,9 @@ class InAppWebViewController {
Map<dynamic, dynamic> headers = argMap["headers"]; Map<dynamic, dynamic> headers = argMap["headers"];
Uint8List body = Uint8List.fromList(argMap["body"].cast<int>()); Uint8List body = Uint8List.fromList(argMap["body"].cast<int>());
String mode = argMap["mode"]; String mode = argMap["mode"];
FetchRequestCredential credentials = FetchRequest.createFetchRequestCredentialFromMap(argMap["credentials"]); FetchRequestCredential credentials =
FetchRequest.createFetchRequestCredentialFromMap(
argMap["credentials"]);
String cache = argMap["cache"]; String cache = argMap["cache"];
String redirect = argMap["redirect"]; String redirect = argMap["redirect"];
String referrer = argMap["referrer"]; String referrer = argMap["referrer"];
@ -714,13 +854,26 @@ class InAppWebViewController {
String integrity = argMap["integrity"]; String integrity = argMap["integrity"];
bool keepalive = argMap["keepalive"]; bool keepalive = argMap["keepalive"];
var request = new FetchRequest(url: url, method: method, headers: headers, body: body, mode: mode, credentials: credentials, var request = new FetchRequest(
cache: cache, redirect: redirect, referrer: referrer, referrerPolicy: referrerPolicy, integrity: integrity, keepalive: keepalive); url: url,
method: method,
headers: headers,
body: body,
mode: mode,
credentials: credentials,
cache: cache,
redirect: redirect,
referrer: referrer,
referrerPolicy: referrerPolicy,
integrity: integrity,
keepalive: keepalive);
if (_widget != null && _widget.shouldInterceptFetchRequest != null) if (_widget != null && _widget.shouldInterceptFetchRequest != null)
return jsonEncode(await _widget.shouldInterceptFetchRequest(this, request)); return jsonEncode(
await _widget.shouldInterceptFetchRequest(this, request));
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
return jsonEncode(await _inAppBrowser.shouldInterceptFetchRequest(request)); return jsonEncode(
await _inAppBrowser.shouldInterceptFetchRequest(request));
return null; return null;
} }
@ -777,10 +930,11 @@ class InAppWebViewController {
Future<String> getHtml() async { Future<String> getHtml() async {
var html = ""; var html = "";
InAppWebViewWidgetOptions options = await getOptions(); InAppWebViewWidgetOptions options = await getOptions();
if (options != null && options.inAppWebViewOptions.javaScriptEnabled == true) { if (options != null &&
html = await evaluateJavascript(source: "window.document.getElementsByTagName('html')[0].outerHTML;"); options.inAppWebViewOptions.javaScriptEnabled == true) {
if (html != null && html.isNotEmpty) html = await evaluateJavascript(
return html; source: "window.document.getElementsByTagName('html')[0].outerHTML;");
if (html != null && html.isNotEmpty) return html;
} }
var webviewUrl = await getUrl(); var webviewUrl = await getUrl();
@ -789,13 +943,13 @@ class InAppWebViewController {
var assetPath = assetPathSplitted[assetPathSplitted.length - 1]; var assetPath = assetPathSplitted[assetPathSplitted.length - 1];
var bytes = await rootBundle.load(assetPath); var bytes = await rootBundle.load(assetPath);
html = utf8.decode(bytes.buffer.asUint8List()); html = utf8.decode(bytes.buffer.asUint8List());
} } else {
else {
HttpClient client = new HttpClient(); HttpClient client = new HttpClient();
var url = Uri.parse(webviewUrl); var url = Uri.parse(webviewUrl);
try { try {
var htmlRequest = await client.getUrl(url); var htmlRequest = await client.getUrl(url);
html = await (await htmlRequest.close()).transform(Utf8Decoder()).join(); html =
await (await htmlRequest.close()).transform(Utf8Decoder()).join();
} catch (e) { } catch (e) {
print(e); print(e);
} }
@ -809,12 +963,14 @@ class InAppWebViewController {
HttpClient client = new HttpClient(); HttpClient client = new HttpClient();
var webviewUrl = await getUrl(); var webviewUrl = await getUrl();
var url = (webviewUrl.startsWith("file:///")) ? Uri.file(webviewUrl) : Uri.parse(webviewUrl); var url = (webviewUrl.startsWith("file:///"))
? Uri.file(webviewUrl)
: Uri.parse(webviewUrl);
String manifestUrl; String manifestUrl;
var html = await getHtml(); var html = await getHtml();
if (html.isEmpty) { if (html.isEmpty) {
return favicons; return favicons;
} }
var assetPathBase; var assetPathBase;
@ -835,14 +991,18 @@ class InAppWebViewController {
if (manifestUrl.startsWith("/")) { if (manifestUrl.startsWith("/")) {
manifestUrl = manifestUrl.substring(1); manifestUrl = manifestUrl.substring(1);
} }
manifestUrl = ((assetPathBase == null) ? url.scheme + "://" + url.host + "/" : assetPathBase) + manifestUrl; manifestUrl = ((assetPathBase == null)
? url.scheme + "://" + url.host + "/"
: assetPathBase) +
manifestUrl;
} }
continue; continue;
} }
if (!attributes["rel"].contains("icon")) { if (!attributes["rel"].contains("icon")) {
continue; continue;
} }
favicons.addAll(_createFavicons(url, assetPathBase, attributes["href"], attributes["rel"], attributes["sizes"], false)); favicons.addAll(_createFavicons(url, assetPathBase, attributes["href"],
attributes["rel"], attributes["sizes"], false));
} }
// try to get /favicon.ico // try to get /favicon.ico
@ -850,7 +1010,7 @@ class InAppWebViewController {
var faviconUrl = url.scheme + "://" + url.host + "/favicon.ico"; var faviconUrl = url.scheme + "://" + url.host + "/favicon.ico";
await client.headUrl(Uri.parse(faviconUrl)); await client.headUrl(Uri.parse(faviconUrl));
favicons.add(Favicon(url: faviconUrl, rel: "shortcut icon")); favicons.add(Favicon(url: faviconUrl, rel: "shortcut icon"));
} catch(e) { } catch (e) {
print("/favicon.ico file not found: " + e.toString()); print("/favicon.ico file not found: " + e.toString());
} }
@ -864,16 +1024,19 @@ class InAppWebViewController {
try { try {
manifestRequest = await client.getUrl(Uri.parse(manifestUrl)); manifestRequest = await client.getUrl(Uri.parse(manifestUrl));
manifestResponse = await manifestRequest.close(); manifestResponse = await manifestRequest.close();
manifestFound = manifestResponse.statusCode == 200 && manifestResponse.headers.contentType?.mimeType == "application/json"; manifestFound = manifestResponse.statusCode == 200 &&
} catch(e) { manifestResponse.headers.contentType?.mimeType == "application/json";
} catch (e) {
print("Manifest file not found: " + e.toString()); print("Manifest file not found: " + e.toString());
} }
if (manifestFound) { if (manifestFound) {
Map<String, dynamic> manifest = json.decode(await manifestResponse.transform(Utf8Decoder()).join()); Map<String, dynamic> manifest =
json.decode(await manifestResponse.transform(Utf8Decoder()).join());
if (manifest.containsKey("icons")) { if (manifest.containsKey("icons")) {
for(Map<String, dynamic> icon in manifest["icons"]) { for (Map<String, dynamic> icon in manifest["icons"]) {
favicons.addAll(_createFavicons(url, assetPathBase, icon["src"], icon["rel"], icon["sizes"], true)); favicons.addAll(_createFavicons(url, assetPathBase, icon["src"],
icon["rel"], icon["sizes"], true));
} }
} }
} }
@ -885,7 +1048,8 @@ class InAppWebViewController {
return url.startsWith("http://") || url.startsWith("https://"); return url.startsWith("http://") || url.startsWith("https://");
} }
List<Favicon> _createFavicons(Uri url, String assetPathBase, String urlIcon, String rel, String sizes, bool isManifest) { List<Favicon> _createFavicons(Uri url, String assetPathBase, String urlIcon,
String rel, String sizes, bool isManifest) {
List<Favicon> favicons = []; List<Favicon> favicons = [];
List<String> urlSplitted = urlIcon.split("/"); List<String> urlSplitted = urlIcon.split("/");
@ -893,17 +1057,26 @@ class InAppWebViewController {
if (urlIcon.startsWith("/")) { if (urlIcon.startsWith("/")) {
urlIcon = urlIcon.substring(1); urlIcon = urlIcon.substring(1);
} }
urlIcon = ((assetPathBase == null) ? url.scheme + "://" + url.host + "/" : assetPathBase) + urlIcon; urlIcon = ((assetPathBase == null)
? url.scheme + "://" + url.host + "/"
: assetPathBase) +
urlIcon;
} }
if (isManifest) { if (isManifest) {
rel = (sizes != null) ? urlSplitted[urlSplitted.length - 1].replaceFirst("-" + sizes, "").split(" ")[0].split(".")[0] : null; rel = (sizes != null)
? urlSplitted[urlSplitted.length - 1]
.replaceFirst("-" + sizes, "")
.split(" ")[0]
.split(".")[0]
: null;
} }
if (sizes != null && sizes.isNotEmpty && sizes != "any") { if (sizes != null && sizes.isNotEmpty && sizes != "any") {
List<String> sizesSplitted = sizes.split(" "); List<String> sizesSplitted = sizes.split(" ");
for (String size in sizesSplitted) { for (String size in sizesSplitted) {
int width = int.parse(size.split("x")[0]); int width = int.parse(size.split("x")[0]);
int height = int.parse(size.split("x")[1]); int height = int.parse(size.split("x")[1]);
favicons.add(Favicon(url: urlIcon, rel: rel, width: width, height: height)); favicons
.add(Favicon(url: urlIcon, rel: rel, width: width, height: height));
} }
} else { } else {
favicons.add(Favicon(url: urlIcon, rel: rel, width: null, height: null)); favicons.add(Favicon(url: urlIcon, rel: rel, width: null, height: null));
@ -913,7 +1086,8 @@ class InAppWebViewController {
} }
///Loads the given [url] with optional [headers] specified as a map from name to value. ///Loads the given [url] with optional [headers] specified as a map from name to value.
Future<void> loadUrl({@required String url, Map<String, String> headers = const {}}) async { Future<void> loadUrl(
{@required String url, Map<String, String> headers = const {}}) async {
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) { if (_inAppBrowserUuid != null && _inAppBrowser != null) {
@ -926,7 +1100,8 @@ class InAppWebViewController {
} }
///Loads the given [url] with [postData] using `POST` method into this WebView. ///Loads the given [url] with [postData] using `POST` method into this WebView.
Future<void> postUrl({@required String url, @required Uint8List postData}) async { Future<void> postUrl(
{@required String url, @required Uint8List postData}) async {
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
assert(postData != null); assert(postData != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
@ -942,7 +1117,11 @@ class InAppWebViewController {
///Loads the given [data] into this WebView, using [baseUrl] as the base URL for the content. ///Loads the given [data] into this WebView, using [baseUrl] as the base URL for the content.
///The [mimeType] parameter specifies the format of the data. ///The [mimeType] parameter specifies the format of the data.
///The [encoding] parameter specifies the encoding of the data. ///The [encoding] parameter specifies the encoding of the data.
Future<void> loadData({@required String data, String mimeType = "text/html", String encoding = "utf8", String baseUrl = "about:blank"}) async { Future<void> loadData(
{@required String data,
String mimeType = "text/html",
String encoding = "utf8",
String baseUrl = "about:blank"}) async {
assert(data != null); assert(data != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) { if (_inAppBrowserUuid != null && _inAppBrowser != null) {
@ -985,7 +1164,9 @@ class InAppWebViewController {
///inAppBrowser.loadFile("assets/index.html"); ///inAppBrowser.loadFile("assets/index.html");
///... ///...
///``` ///```
Future<void> loadFile({@required String assetFilePath, Map<String, String> headers = const {}}) async { Future<void> loadFile(
{@required String assetFilePath,
Map<String, String> headers = const {}}) async {
assert(assetFilePath != null && assetFilePath.isNotEmpty); assert(assetFilePath != null && assetFilePath.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) { if (_inAppBrowserUuid != null && _inAppBrowser != null) {
@ -1107,8 +1288,7 @@ class InAppWebViewController {
} }
args.putIfAbsent('source', () => source); args.putIfAbsent('source', () => source);
var data = await _channel.invokeMethod('evaluateJavascript', args); var data = await _channel.invokeMethod('evaluateJavascript', args);
if (data != null && Platform.isAndroid) if (data != null && Platform.isAndroid) data = json.decode(data);
data = json.decode(data);
return data; return data;
} }
@ -1122,9 +1302,10 @@ class InAppWebViewController {
args.putIfAbsent('urlFile', () => urlFile); args.putIfAbsent('urlFile', () => urlFile);
await _channel.invokeMethod('injectJavascriptFileFromUrl', args); await _channel.invokeMethod('injectJavascriptFileFromUrl', args);
} }
///Injects a JavaScript file into the WebView from the flutter assets directory. ///Injects a JavaScript file into the WebView from the flutter assets directory.
Future<void> injectJavascriptFileFromAsset({@required String assetFilePath}) async { Future<void> injectJavascriptFileFromAsset(
{@required String assetFilePath}) async {
String source = await rootBundle.loadString(assetFilePath); String source = await rootBundle.loadString(assetFilePath);
await evaluateJavascript(source: source); await evaluateJavascript(source: source);
} }
@ -1201,7 +1382,9 @@ class InAppWebViewController {
/// }); /// });
/// """); /// """);
///``` ///```
void addJavaScriptHandler({@required String handlerName, @required JavaScriptHandlerCallback callback}) { void addJavaScriptHandler(
{@required String handlerName,
@required JavaScriptHandlerCallback callback}) {
assert(!javaScriptHandlerForbiddenNames.contains(handlerName)); assert(!javaScriptHandlerForbiddenNames.contains(handlerName));
this.javaScriptHandlersMap[handlerName] = (callback); this.javaScriptHandlersMap[handlerName] = (callback);
} }
@ -1209,7 +1392,8 @@ class InAppWebViewController {
///Removes a JavaScript message handler previously added with the [addJavaScriptHandler()] associated to [handlerName] key. ///Removes a JavaScript message handler previously added with the [addJavaScriptHandler()] associated to [handlerName] key.
///Returns the value associated with [handlerName] before it was removed. ///Returns the value associated with [handlerName] before it was removed.
///Returns `null` if [handlerName] was not found. ///Returns `null` if [handlerName] was not found.
JavaScriptHandlerCallback removeJavaScriptHandler({@required String handlerName}) { JavaScriptHandlerCallback removeJavaScriptHandler(
{@required String handlerName}) {
return this.javaScriptHandlersMap.remove(handlerName); return this.javaScriptHandlersMap.remove(handlerName);
} }
@ -1252,15 +1436,20 @@ class InAppWebViewController {
args.putIfAbsent('uuid', () => _inAppBrowserUuid); args.putIfAbsent('uuid', () => _inAppBrowserUuid);
} }
InAppWebViewWidgetOptions inAppWebViewWidgetOptions = InAppWebViewWidgetOptions(); InAppWebViewWidgetOptions inAppWebViewWidgetOptions =
Map<dynamic, dynamic> options = await _channel.invokeMethod('getOptions', args); InAppWebViewWidgetOptions();
Map<dynamic, dynamic> options =
await _channel.invokeMethod('getOptions', args);
if (options != null) { if (options != null) {
options = options.cast<String, dynamic>(); options = options.cast<String, dynamic>();
inAppWebViewWidgetOptions.inAppWebViewOptions = InAppWebViewOptions.fromMap(options); inAppWebViewWidgetOptions.inAppWebViewOptions =
InAppWebViewOptions.fromMap(options);
if (Platform.isAndroid) if (Platform.isAndroid)
inAppWebViewWidgetOptions.androidInAppWebViewOptions = AndroidInAppWebViewOptions.fromMap(options); inAppWebViewWidgetOptions.androidInAppWebViewOptions =
AndroidInAppWebViewOptions.fromMap(options);
else if (Platform.isIOS) else if (Platform.isIOS)
inAppWebViewWidgetOptions.iosInAppWebViewOptions = IosInAppWebViewOptions.fromMap(options); inAppWebViewWidgetOptions.iosInAppWebViewOptions =
IosInAppWebViewOptions.fromMap(options);
} }
return inAppWebViewWidgetOptions; return inAppWebViewWidgetOptions;
@ -1276,7 +1465,8 @@ class InAppWebViewController {
_inAppBrowser.throwIsNotOpened(); _inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid); args.putIfAbsent('uuid', () => _inAppBrowserUuid);
} }
Map<dynamic, dynamic> result = await _channel.invokeMethod('getCopyBackForwardList', args); Map<dynamic, dynamic> result =
await _channel.invokeMethod('getCopyBackForwardList', args);
result = result.cast<String, dynamic>(); result = result.cast<String, dynamic>();
List<dynamic> historyListMap = result["history"]; List<dynamic> historyListMap = result["history"];
@ -1285,9 +1475,14 @@ class InAppWebViewController {
int currentIndex = result["currentIndex"]; int currentIndex = result["currentIndex"];
List<WebHistoryItem> historyList = List(); List<WebHistoryItem> historyList = List();
for(var i = 0; i < historyListMap.length; i++) { for (var i = 0; i < historyListMap.length; i++) {
LinkedHashMap<dynamic, dynamic> historyItem = historyListMap[i]; LinkedHashMap<dynamic, dynamic> historyItem = historyListMap[i];
historyList.add(WebHistoryItem(originalUrl: historyItem["originalUrl"], title: historyItem["title"], url: historyItem["url"], index: i, offset: i - currentIndex)); historyList.add(WebHistoryItem(
originalUrl: historyItem["originalUrl"],
title: historyItem["title"],
url: historyItem["url"],
index: i,
offset: i - currentIndex));
} }
return WebHistory(list: historyList, currentIndex: currentIndex); return WebHistory(list: historyList, currentIndex: currentIndex);
} }
@ -1434,12 +1629,14 @@ class InAppWebViewController {
///Gets the html (with javascript) of the Chromium's t-rex runner game. Used in combination with [getTRexRunnerCss()]. ///Gets the html (with javascript) of the Chromium's t-rex runner game. Used in combination with [getTRexRunnerCss()].
Future<String> getTRexRunnerHtml() async { Future<String> getTRexRunnerHtml() async {
return await rootBundle.loadString("packages/flutter_inappwebview/t_rex_runner/t-rex.html"); return await rootBundle
.loadString("packages/flutter_inappwebview/t_rex_runner/t-rex.html");
} }
///Gets the css of the Chromium's t-rex runner game. Used in combination with [getTRexRunnerHtml()]. ///Gets the css of the Chromium's t-rex runner game. Used in combination with [getTRexRunnerHtml()].
Future<String> getTRexRunnerCss() async { Future<String> getTRexRunnerCss() async {
return await rootBundle.loadString("packages/flutter_inappwebview/t_rex_runner/t-rex.css"); return await rootBundle
.loadString("packages/flutter_inappwebview/t_rex_runner/t-rex.css");
} }
///Scrolls the WebView to the position. ///Scrolls the WebView to the position.

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@ import 'content_blocker.dart';
import 'types.dart'; import 'types.dart';
class AndroidOptions {} class AndroidOptions {}
class IosOptions {} class IosOptions {}
class WebViewOptions { class WebViewOptions {
@ -37,85 +38,130 @@ class ChromeSafariBrowserOptions {
} }
///This class represents all the cross-platform WebView options available. ///This class represents all the cross-platform WebView options available.
class InAppWebViewOptions implements WebViewOptions, BrowserOptions, AndroidOptions, IosOptions { class InAppWebViewOptions
implements WebViewOptions, BrowserOptions, AndroidOptions, IosOptions {
///Set to `true` to be able to listen at the [shouldOverrideUrlLoading] event. The default value is `false`. ///Set to `true` to be able to listen at the [shouldOverrideUrlLoading] event. The default value is `false`.
bool useShouldOverrideUrlLoading; bool useShouldOverrideUrlLoading;
///Set to `true` to be able to listen at the [onLoadResource] event. The default value is `false`. ///Set to `true` to be able to listen at the [onLoadResource] event. The default value is `false`.
bool useOnLoadResource; bool useOnLoadResource;
///Set to `true` to be able to listen at the [onDownloadStart] event. The default value is `false`. ///Set to `true` to be able to listen at the [onDownloadStart] event. The default value is `false`.
bool useOnDownloadStart; bool useOnDownloadStart;
///Set to `true` to be able to listen at the [onTargetBlank] event. The default value is `false`. ///Set to `true` to be able to listen at the [onTargetBlank] event. The default value is `false`.
bool useOnTargetBlank; bool useOnTargetBlank;
///Set to `true` to have all the browser's cache cleared before the new window is opened. The default value is `false`. ///Set to `true` to have all the browser's cache cleared before the new window is opened. The default value is `false`.
bool clearCache; bool clearCache;
///Sets the user-agent for the WebView. ///Sets the user-agent for the WebView.
/// ///
///**NOTE**: available on iOS 9.0+. ///**NOTE**: available on iOS 9.0+.
String userAgent; String userAgent;
///Append to the existing user-agent. Setting userAgent will override this. ///Append to the existing user-agent. Setting userAgent will override this.
/// ///
///**NOTE**: available on Android 17+ and on iOS 9.0+. ///**NOTE**: available on Android 17+ and on iOS 9.0+.
String applicationNameForUserAgent; String applicationNameForUserAgent;
///Set to `true` to enable JavaScript. The default value is `true`. ///Set to `true` to enable JavaScript. The default value is `true`.
bool javaScriptEnabled; bool javaScriptEnabled;
///Enables debugging of web contents (HTML / CSS / JavaScript) loaded into any WebViews of this application. ///Enables debugging of web contents (HTML / CSS / JavaScript) loaded into any WebViews of this application.
///This flag can be enabled in order to facilitate debugging of web layouts and JavaScript code running inside WebViews. The default is `false`. ///This flag can be enabled in order to facilitate debugging of web layouts and JavaScript code running inside WebViews. The default is `false`.
/// ///
///**NOTE**: on iOS the debugging mode is always enabled. ///**NOTE**: on iOS the debugging mode is always enabled.
bool debuggingEnabled; bool debuggingEnabled;
///Set to `true` to allow JavaScript open windows without user interaction. The default value is `false`. ///Set to `true` to allow JavaScript open windows without user interaction. The default value is `false`.
bool javaScriptCanOpenWindowsAutomatically; bool javaScriptCanOpenWindowsAutomatically;
///Set to `true` to prevent HTML5 audio or video from autoplaying. The default value is `true`. ///Set to `true` to prevent HTML5 audio or video from autoplaying. The default value is `true`.
/// ///
///**NOTE**: available on iOS 10.0+. ///**NOTE**: available on iOS 10.0+.
bool mediaPlaybackRequiresUserGesture; bool mediaPlaybackRequiresUserGesture;
///Sets the minimum font size. The default value is `8` for Android, `0` for iOS. ///Sets the minimum font size. The default value is `8` for Android, `0` for iOS.
int minimumFontSize; int minimumFontSize;
///Define whether the vertical scrollbar should be drawn or not. The default value is `true`. ///Define whether the vertical scrollbar should be drawn or not. The default value is `true`.
bool verticalScrollBarEnabled; bool verticalScrollBarEnabled;
///Define whether the horizontal scrollbar should be drawn or not. The default value is `true`. ///Define whether the horizontal scrollbar should be drawn or not. The default value is `true`.
bool horizontalScrollBarEnabled; bool horizontalScrollBarEnabled;
///List of custom schemes that the WebView must handle. Use the [onLoadResourceCustomScheme] event to intercept resource requests with custom scheme. ///List of custom schemes that the WebView must handle. Use the [onLoadResourceCustomScheme] event to intercept resource requests with custom scheme.
/// ///
///**NOTE**: available on iOS 11.0+. ///**NOTE**: available on iOS 11.0+.
List<String> resourceCustomSchemes; List<String> resourceCustomSchemes;
///List of [ContentBlocker] that are a set of rules used to block content in the browser window. ///List of [ContentBlocker] that are a set of rules used to block content in the browser window.
/// ///
///**NOTE**: available on iOS 11.0+. ///**NOTE**: available on iOS 11.0+.
List<ContentBlocker> contentBlockers; List<ContentBlocker> contentBlockers;
///Sets the content mode that the WebView needs to use when loading and rendering a webpage. The default value is [InAppWebViewUserPreferredContentMode.RECOMMENDED]. ///Sets the content mode that the WebView needs to use when loading and rendering a webpage. The default value is [InAppWebViewUserPreferredContentMode.RECOMMENDED].
/// ///
///**NOTE**: available on iOS 13.0+. ///**NOTE**: available on iOS 13.0+.
InAppWebViewUserPreferredContentMode preferredContentMode; InAppWebViewUserPreferredContentMode preferredContentMode;
///Set to `true` to be able to listen at the [shouldInterceptAjaxRequest] event. The default value is `false`. ///Set to `true` to be able to listen at the [shouldInterceptAjaxRequest] event. The default value is `false`.
bool useShouldInterceptAjaxRequest; bool useShouldInterceptAjaxRequest;
///Set to `true` to be able to listen at the [shouldInterceptFetchRequest] event. The default value is `false`. ///Set to `true` to be able to listen at the [shouldInterceptFetchRequest] event. The default value is `false`.
bool useShouldInterceptFetchRequest; bool useShouldInterceptFetchRequest;
///Set to `true` to open a browser window with incognito mode. The default value is `false`. ///Set to `true` to open a browser window with incognito mode. The default value is `false`.
/// ///
///**NOTE**: available on iOS 9.0+. ///**NOTE**: available on iOS 9.0+.
bool incognito; bool incognito;
///Sets whether WebView should use browser caching. The default value is `true`. ///Sets whether WebView should use browser caching. The default value is `true`.
/// ///
///**NOTE**: available on iOS 9.0+. ///**NOTE**: available on iOS 9.0+.
bool cacheEnabled; bool cacheEnabled;
///Set to `true` to make the background of the WebView transparent. If your app has a dark theme, this can prevent a white flash on initialization. The default value is `false`. ///Set to `true` to make the background of the WebView transparent. If your app has a dark theme, this can prevent a white flash on initialization. The default value is `false`.
bool transparentBackground; bool transparentBackground;
///Set to `true` to disable vertical scroll. The default value is `false`. ///Set to `true` to disable vertical scroll. The default value is `false`.
bool disableVerticalScroll; bool disableVerticalScroll;
///Set to `true` to disable horizontal scroll. The default value is `false`. ///Set to `true` to disable horizontal scroll. The default value is `false`.
bool disableHorizontalScroll; bool disableHorizontalScroll;
InAppWebViewOptions({this.useShouldOverrideUrlLoading = false, this.useOnLoadResource = false, this.useOnDownloadStart = false, this.useOnTargetBlank = false, InAppWebViewOptions(
this.clearCache = false, this.userAgent = "", this.applicationNameForUserAgent = "", this.javaScriptEnabled = true, this.debuggingEnabled = false, this.javaScriptCanOpenWindowsAutomatically = false, {this.useShouldOverrideUrlLoading = false,
this.mediaPlaybackRequiresUserGesture = true, this.minimumFontSize, this.verticalScrollBarEnabled = true, this.horizontalScrollBarEnabled = true, this.useOnLoadResource = false,
this.resourceCustomSchemes = const [], this.contentBlockers = const [], this.preferredContentMode = InAppWebViewUserPreferredContentMode.RECOMMENDED, this.useOnDownloadStart = false,
this.useShouldInterceptAjaxRequest = false, this.useShouldInterceptFetchRequest = false, this.incognito = false, this.cacheEnabled = true, this.transparentBackground = false, this.useOnTargetBlank = false,
this.disableVerticalScroll = false, this.disableHorizontalScroll = false}) { this.clearCache = false,
if (this.minimumFontSize == null) this.userAgent = "",
this.minimumFontSize = Platform.isAndroid ? 8 : 0; this.applicationNameForUserAgent = "",
assert(!this.resourceCustomSchemes.contains("http") && !this.resourceCustomSchemes.contains("https")); this.javaScriptEnabled = true,
} this.debuggingEnabled = false,
this.javaScriptCanOpenWindowsAutomatically = false,
this.mediaPlaybackRequiresUserGesture = true,
this.minimumFontSize,
this.verticalScrollBarEnabled = true,
this.horizontalScrollBarEnabled = true,
this.resourceCustomSchemes = const [],
this.contentBlockers = const [],
this.preferredContentMode =
InAppWebViewUserPreferredContentMode.RECOMMENDED,
this.useShouldInterceptAjaxRequest = false,
this.useShouldInterceptFetchRequest = false,
this.incognito = false,
this.cacheEnabled = true,
this.transparentBackground = false,
this.disableVerticalScroll = false,
this.disableHorizontalScroll = false}) {
if (this.minimumFontSize == null)
this.minimumFontSize = Platform.isAndroid ? 8 : 0;
assert(!this.resourceCustomSchemes.contains("http") &&
!this.resourceCustomSchemes.contains("https"));
}
@override @override
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
@ -134,7 +180,8 @@ class InAppWebViewOptions implements WebViewOptions, BrowserOptions, AndroidOpti
"applicationNameForUserAgent": applicationNameForUserAgent, "applicationNameForUserAgent": applicationNameForUserAgent,
"javaScriptEnabled": javaScriptEnabled, "javaScriptEnabled": javaScriptEnabled,
"debuggingEnabled": debuggingEnabled, "debuggingEnabled": debuggingEnabled,
"javaScriptCanOpenWindowsAutomatically": javaScriptCanOpenWindowsAutomatically, "javaScriptCanOpenWindowsAutomatically":
javaScriptCanOpenWindowsAutomatically,
"mediaPlaybackRequiresUserGesture": mediaPlaybackRequiresUserGesture, "mediaPlaybackRequiresUserGesture": mediaPlaybackRequiresUserGesture,
"verticalScrollBarEnabled": verticalScrollBarEnabled, "verticalScrollBarEnabled": verticalScrollBarEnabled,
"horizontalScrollBarEnabled": horizontalScrollBarEnabled, "horizontalScrollBarEnabled": horizontalScrollBarEnabled,
@ -151,15 +198,14 @@ class InAppWebViewOptions implements WebViewOptions, BrowserOptions, AndroidOpti
}; };
} }
@override
static InAppWebViewOptions fromMap(Map<String, dynamic> map) { static InAppWebViewOptions fromMap(Map<String, dynamic> map) {
List<ContentBlocker> contentBlockers = []; List<ContentBlocker> contentBlockers = [];
List<dynamic> contentBlockersMapList = map["contentBlockers"]; List<dynamic> contentBlockersMapList = map["contentBlockers"];
if (contentBlockersMapList != null) { if (contentBlockersMapList != null) {
contentBlockersMapList.forEach((contentBlocker) { contentBlockersMapList.forEach((contentBlocker) {
contentBlockers.add(ContentBlocker.fromMap( contentBlockers.add(ContentBlocker.fromMap(
Map<dynamic, Map<dynamic, dynamic>>.from(Map<dynamic, dynamic>.from(contentBlocker)) Map<dynamic, Map<dynamic, dynamic>>.from(
)); Map<dynamic, dynamic>.from(contentBlocker))));
}); });
} }
@ -173,15 +219,22 @@ class InAppWebViewOptions implements WebViewOptions, BrowserOptions, AndroidOpti
options.applicationNameForUserAgent = map["applicationNameForUserAgent"]; options.applicationNameForUserAgent = map["applicationNameForUserAgent"];
options.javaScriptEnabled = map["javaScriptEnabled"]; options.javaScriptEnabled = map["javaScriptEnabled"];
options.debuggingEnabled = map["debuggingEnabled"]; options.debuggingEnabled = map["debuggingEnabled"];
options.javaScriptCanOpenWindowsAutomatically = map["javaScriptCanOpenWindowsAutomatically"]; options.javaScriptCanOpenWindowsAutomatically =
options.mediaPlaybackRequiresUserGesture = map["mediaPlaybackRequiresUserGesture"]; map["javaScriptCanOpenWindowsAutomatically"];
options.mediaPlaybackRequiresUserGesture =
map["mediaPlaybackRequiresUserGesture"];
options.verticalScrollBarEnabled = map["verticalScrollBarEnabled"]; options.verticalScrollBarEnabled = map["verticalScrollBarEnabled"];
options.horizontalScrollBarEnabled = map["horizontalScrollBarEnabled"]; options.horizontalScrollBarEnabled = map["horizontalScrollBarEnabled"];
options.resourceCustomSchemes = List<String>.from(map["resourceCustomSchemes"] ?? []); options.resourceCustomSchemes =
List<String>.from(map["resourceCustomSchemes"] ?? []);
options.contentBlockers = contentBlockers; options.contentBlockers = contentBlockers;
options.preferredContentMode = InAppWebViewUserPreferredContentMode.fromValue(map["preferredContentMode"]); options.preferredContentMode =
options.useShouldInterceptAjaxRequest = map["useShouldInterceptAjaxRequest"]; InAppWebViewUserPreferredContentMode.fromValue(
options.useShouldInterceptFetchRequest = map["useShouldInterceptFetchRequest"]; map["preferredContentMode"]);
options.useShouldInterceptAjaxRequest =
map["useShouldInterceptAjaxRequest"];
options.useShouldInterceptFetchRequest =
map["useShouldInterceptFetchRequest"];
options.incognito = map["incognito"]; options.incognito = map["incognito"];
options.cacheEnabled = map["cacheEnabled"]; options.cacheEnabled = map["cacheEnabled"];
options.transparentBackground = map["transparentBackground"]; options.transparentBackground = map["transparentBackground"];
@ -192,92 +245,124 @@ class InAppWebViewOptions implements WebViewOptions, BrowserOptions, AndroidOpti
} }
///This class represents all the Android-only WebView options available. ///This class represents all the Android-only WebView options available.
class AndroidInAppWebViewOptions implements WebViewOptions, BrowserOptions, AndroidOptions { class AndroidInAppWebViewOptions
implements WebViewOptions, BrowserOptions, AndroidOptions {
///Sets the text zoom of the page in percent. The default value is `100`. ///Sets the text zoom of the page in percent. The default value is `100`.
int textZoom; int textZoom;
///Set to `true` to have the session cookie cache cleared before the new window is opened. ///Set to `true` to have the session cookie cache cleared before the new window is opened.
bool clearSessionCache; bool clearSessionCache;
///Set to `true` if the WebView should use its built-in zoom mechanisms. The default value is `false`. ///Set to `true` if the WebView should use its built-in zoom mechanisms. The default value is `false`.
bool builtInZoomControls; bool builtInZoomControls;
///Set to `true` if the WebView should display on-screen zoom controls when using the built-in zoom mechanisms. The default value is `false`. ///Set to `true` if the WebView should display on-screen zoom controls when using the built-in zoom mechanisms. The default value is `false`.
bool displayZoomControls; bool displayZoomControls;
///Set to `false` if the WebView should not support zooming using its on-screen zoom controls and gestures. The default value is `true`. ///Set to `false` if the WebView should not support zooming using its on-screen zoom controls and gestures. The default value is `true`.
bool supportZoom; bool supportZoom;
///Set to `true` if you want the database storage API is enabled. The default value is `false`. ///Set to `true` if you want the database storage API is enabled. The default value is `false`.
bool databaseEnabled; bool databaseEnabled;
///Set to `true` if you want the DOM storage API is enabled. The default value is `false`. ///Set to `true` if you want the DOM storage API is enabled. The default value is `false`.
bool domStorageEnabled; bool domStorageEnabled;
///Set to `true` if the WebView should enable support for the "viewport" HTML meta tag or should use a wide viewport. ///Set to `true` if the WebView should enable support for the "viewport" HTML meta tag or should use a wide viewport.
///When the value of the setting is false, the layout width is always set to the width of the WebView control in device-independent (CSS) pixels. ///When the value of the setting is false, the layout width is always set to the width of the WebView control in device-independent (CSS) pixels.
///When the value is true and the page contains the viewport meta tag, the value of the width specified in the tag is used. ///When the value is true and the page contains the viewport meta tag, the value of the width specified in the tag is used.
///If the page does not contain the tag or does not provide a width, then a wide viewport will be used. The default value is `true`. ///If the page does not contain the tag or does not provide a width, then a wide viewport will be used. The default value is `true`.
bool useWideViewPort; bool useWideViewPort;
///Sets whether Safe Browsing is enabled. Safe Browsing allows WebView to protect against malware and phishing attacks by verifying the links. ///Sets whether Safe Browsing is enabled. Safe Browsing allows WebView to protect against malware and phishing attacks by verifying the links.
///Safe Browsing is enabled by default for devices which support it. ///Safe Browsing is enabled by default for devices which support it.
/// ///
///**NOTE**: available on Android 26+. ///**NOTE**: available on Android 26+.
bool safeBrowsingEnabled; bool safeBrowsingEnabled;
///Configures the WebView's behavior when a secure origin attempts to load a resource from an insecure origin. ///Configures 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+.
AndroidInAppWebViewMixedContentMode mixedContentMode; AndroidInAppWebViewMixedContentMode mixedContentMode;
///Enables or disables content URL access within WebView. Content URL access allows WebView to load content from a content provider installed in the system. The default value is `true`. ///Enables or disables content URL access within WebView. Content URL access allows WebView to load content from a content provider installed in the system. The default value is `true`.
bool allowContentAccess; bool allowContentAccess;
///Enables or disables file access within WebView. Note that this enables or disables file system access only. ///Enables or disables file access within WebView. Note that this enables or disables file system access only.
///Assets and resources are still accessible using \file:///android_asset` and `file:///android_res`. The default value is `true`. ///Assets and resources are still accessible using \file:///android_asset` and `file:///android_res`. The default value is `true`.
bool allowFileAccess; bool allowFileAccess;
///Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from other file scheme URLs. ///Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from other file scheme URLs.
///Note that the value of this setting is ignored if the value of [allowFileAccessFromFileURLs] is `true`. ///Note that the value of this setting is ignored if the value of [allowFileAccessFromFileURLs] is `true`.
///Note too, that this setting affects only JavaScript access to file scheme resources. The default value is `false`. ///Note too, that this setting affects only JavaScript access to file scheme resources. The default value is `false`.
bool allowFileAccessFromFileURLs; bool allowFileAccessFromFileURLs;
///Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from any origin. ///Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from any origin.
///Note that this setting affects only JavaScript access to file scheme resources. ///Note that this setting affects only JavaScript access to file scheme resources.
///This includes access to content from other file scheme URLs. The default value is `false`. ///This includes access to content from other file scheme URLs. The default value is `false`.
bool allowUniversalAccessFromFileURLs; bool allowUniversalAccessFromFileURLs;
///Sets the path to the Application Caches files. In order for the Application Caches API to be enabled, this option must be set a path to which the application can write. ///Sets the path to the Application Caches files. In order for the Application Caches API to be enabled, this option must be set a path to which the application can write.
///This option is used one time: repeated calls are ignored. ///This option is used one time: repeated calls are ignored.
String appCachePath; String appCachePath;
///Sets whether the WebView should not load image resources from the network (resources accessed via http and https URI schemes). The default value is `false`. ///Sets whether the WebView should not load image resources from the network (resources accessed via http and https URI schemes). The default value is `false`.
bool blockNetworkImage; bool blockNetworkImage;
///Sets whether the WebView should not load resources from the network. The default value is `false`. ///Sets whether the WebView should not load resources from the network. The default value is `false`.
bool blockNetworkLoads; bool blockNetworkLoads;
///Overrides the way the cache is used. The way the cache is used is based on the navigation type. For a normal page load, the cache is checked and content is re-validated as needed. ///Overrides the way the cache is used. The way the cache is used is based on the navigation type. For a normal page load, the cache is checked and content is re-validated as needed.
///When navigating back, content is not revalidated, instead the content is just retrieved from the cache. The default value is [AndroidInAppWebViewCacheMode.LOAD_DEFAULT]. ///When navigating back, content is not revalidated, instead the content is just retrieved from the cache. The default value is [AndroidInAppWebViewCacheMode.LOAD_DEFAULT].
AndroidInAppWebViewCacheMode cacheMode; AndroidInAppWebViewCacheMode cacheMode;
///Sets the cursive font family name. The default value is `"cursive"`. ///Sets the cursive font family name. The default value is `"cursive"`.
String cursiveFontFamily; String cursiveFontFamily;
///Sets the default fixed font size. The default value is `16`. ///Sets the default fixed font size. The default value is `16`.
int defaultFixedFontSize; int defaultFixedFontSize;
///Sets the default font size. The default value is `16`. ///Sets the default font size. The default value is `16`.
int defaultFontSize; int defaultFontSize;
///Sets the default text encoding name to use when decoding html pages. The default value is `"UTF-8"`. ///Sets the default text encoding name to use when decoding html pages. The default value is `"UTF-8"`.
String defaultTextEncodingName; String defaultTextEncodingName;
///Disables the action mode menu items according to menuItems flag. ///Disables the action mode menu items according to menuItems flag.
/// ///
///**NOTE**: available on Android 24+. ///**NOTE**: available on Android 24+.
AndroidInAppWebViewModeMenuItem disabledActionModeMenuItems; AndroidInAppWebViewModeMenuItem disabledActionModeMenuItems;
///Sets the fantasy font family name. The default value is `"fantasy"`. ///Sets the fantasy font family name. The default value is `"fantasy"`.
String fantasyFontFamily; String fantasyFontFamily;
///Sets the fixed font family name. The default value is `"monospace"`. ///Sets the fixed font family name. The default value is `"monospace"`.
String fixedFontFamily; String fixedFontFamily;
///Set the force dark mode for this WebView. The default value is [AndroidInAppWebViewForceDark.FORCE_DARK_OFF]. ///Set the force dark mode for this WebView. The default value is [AndroidInAppWebViewForceDark.FORCE_DARK_OFF].
/// ///
///**NOTE**: available on Android 29+. ///**NOTE**: available on Android 29+.
AndroidInAppWebViewForceDark forceDark; AndroidInAppWebViewForceDark forceDark;
///Sets whether Geolocation API is enabled. The default value is `true`. ///Sets whether Geolocation API is enabled. The default value is `true`.
bool geolocationEnabled; bool geolocationEnabled;
///Sets the underlying layout algorithm. This will cause a re-layout of the WebView. ///Sets the underlying layout algorithm. This will cause a re-layout of the WebView.
AndroidInAppWebViewLayoutAlgorithm layoutAlgorithm; AndroidInAppWebViewLayoutAlgorithm layoutAlgorithm;
///Sets whether the WebView loads pages in overview mode, that is, zooms out the content to fit on screen by width. ///Sets whether the WebView loads pages in overview mode, that is, zooms out the content to fit on screen by width.
///This setting is taken into account when the content width is greater than the width of the WebView control, for example, when [useWideViewPort] is enabled. ///This setting is taken into account when the content width is greater than the width of the WebView control, for example, when [useWideViewPort] is enabled.
///The default value is `false`. ///The default value is `false`.
bool loadWithOverviewMode; bool loadWithOverviewMode;
///Sets whether the WebView should load image resources. Note that this method controls loading of all images, including those embedded using the data URI scheme. ///Sets whether the WebView should load image resources. Note that this method controls loading of all images, including those embedded using the data URI scheme.
///Note that if the value of this setting is changed from false to true, all images resources referenced by content currently displayed by the WebView are loaded automatically. ///Note that if the value of this setting is changed from false to true, all images resources referenced by content currently displayed by the WebView are loaded automatically.
///The default value is `true`. ///The default value is `true`.
bool loadsImagesAutomatically; bool loadsImagesAutomatically;
///Sets the minimum logical font size. The default is `8`. ///Sets the minimum logical font size. The default is `8`.
int minimumLogicalFontSize; int minimumLogicalFontSize;
///Sets the initial scale for this WebView. 0 means default. The behavior for the default scale depends on the state of [useWideViewPort] and [loadWithOverviewMode]. ///Sets the initial scale for this WebView. 0 means default. The behavior for the default scale depends on the state of [useWideViewPort] and [loadWithOverviewMode].
///If the content fits into the WebView control by width, then the zoom is set to 100%. For wide content, the behavior depends on the state of [loadWithOverviewMode]. ///If the content fits into the WebView control by width, then the zoom is set to 100%. For wide content, the behavior depends on the state of [loadWithOverviewMode].
///If its value is true, the content will be zoomed out to be fit by width into the WebView control, otherwise not. ///If its value is true, the content will be zoomed out to be fit by width into the WebView control, otherwise not.
@ -285,43 +370,82 @@ class AndroidInAppWebViewOptions implements WebViewOptions, BrowserOptions, Andr
///Please note that unlike the scale properties in the viewport meta tag, this method doesn't take the screen density into account. ///Please note that unlike the scale properties in the viewport meta tag, this method doesn't take the screen density into account.
///The default is `0`. ///The default is `0`.
int initialScale; int initialScale;
///Tells the WebView whether it needs to set a node. The default value is `true`. ///Tells the WebView whether it needs to set a node. The default value is `true`.
bool needInitialFocus; bool needInitialFocus;
///Sets whether this WebView should raster tiles when it is offscreen but attached to a window. ///Sets whether this WebView should raster tiles when it is offscreen but attached to a window.
///Turning this on can avoid rendering artifacts when animating an offscreen WebView on-screen. ///Turning this on can avoid rendering artifacts when animating an offscreen WebView on-screen.
///Offscreen WebViews in this mode use more memory. The default value is `false`. ///Offscreen WebViews in this mode use more memory. The default value is `false`.
/// ///
///**NOTE**: available on Android 23+. ///**NOTE**: available on Android 23+.
bool offscreenPreRaster; bool offscreenPreRaster;
///Sets the sans-serif font family name. The default value is `"sans-serif"`. ///Sets the sans-serif font family name. The default value is `"sans-serif"`.
String sansSerifFontFamily; String sansSerifFontFamily;
///Sets the serif font family name. The default value is `"sans-serif"`. ///Sets the serif font family name. The default value is `"sans-serif"`.
String serifFontFamily; String serifFontFamily;
///Sets the standard font family name. The default value is `"sans-serif"`. ///Sets the standard font family name. The default value is `"sans-serif"`.
String standardFontFamily; String standardFontFamily;
///Sets whether the WebView should save form data. In Android O, the platform has implemented a fully functional Autofill feature to store form data. ///Sets whether the WebView should save form data. In Android O, the platform has implemented a fully functional Autofill feature to store form data.
///Therefore, the Webview form data save feature is disabled. Note that the feature will continue to be supported on older versions of Android as before. ///Therefore, the Webview form data save feature is disabled. Note that the feature will continue to be supported on older versions of Android as before.
bool saveFormData; bool saveFormData;
///Boolean value to enable third party cookies in the WebView. ///Boolean value to enable third party cookies in the WebView.
///Used on Android Lollipop and above only as third party cookies are enabled by default on Android Kitkat and below and on iOS. ///Used on Android Lollipop and above only as third party cookies are enabled by default on Android Kitkat and below and on iOS.
///The default value is `true`. ///The default value is `true`.
/// ///
///**NOTE**: available on Android 21+. ///**NOTE**: available on Android 21+.
bool thirdPartyCookiesEnabled; bool thirdPartyCookiesEnabled;
///Boolean value to enable Hardware Acceleration in the WebView. ///Boolean value to enable Hardware Acceleration in the WebView.
///The default value is `true`. ///The default value is `true`.
bool hardwareAcceleration; bool hardwareAcceleration;
AndroidInAppWebViewOptions({this.textZoom = 100, this.clearSessionCache = false, this.builtInZoomControls = false, this.displayZoomControls = false, this.supportZoom = true, this.databaseEnabled = false, AndroidInAppWebViewOptions(
this.domStorageEnabled = false, this.useWideViewPort = true, this.safeBrowsingEnabled = true, this.mixedContentMode, {this.textZoom = 100,
this.allowContentAccess = true, this.allowFileAccess = true, this.allowFileAccessFromFileURLs = false, this.allowUniversalAccessFromFileURLs = false, this.clearSessionCache = false,
this.appCachePath, this.blockNetworkImage = false, this.blockNetworkLoads = false, this.cacheMode = AndroidInAppWebViewCacheMode.LOAD_DEFAULT, this.builtInZoomControls = false,
this.cursiveFontFamily = "cursive", this.defaultFixedFontSize = 16, this.defaultFontSize = 16, this.defaultTextEncodingName = "UTF-8", this.displayZoomControls = false,
this.disabledActionModeMenuItems, this.fantasyFontFamily = "fantasy", this.fixedFontFamily = "monospace", this.forceDark = AndroidInAppWebViewForceDark.FORCE_DARK_OFF, this.supportZoom = true,
this.geolocationEnabled = true, this.layoutAlgorithm, this.loadWithOverviewMode = true, this.loadsImagesAutomatically = true, this.databaseEnabled = false,
this.minimumLogicalFontSize = 8, this.needInitialFocus = true, this.offscreenPreRaster = false, this.sansSerifFontFamily = "sans-serif", this.serifFontFamily = "sans-serif", this.domStorageEnabled = false,
this.standardFontFamily = "sans-serif", this.saveFormData = true, this.thirdPartyCookiesEnabled = true, this.hardwareAcceleration = true, this.initialScale = 0 this.useWideViewPort = true,
}); this.safeBrowsingEnabled = true,
this.mixedContentMode,
this.allowContentAccess = true,
this.allowFileAccess = true,
this.allowFileAccessFromFileURLs = false,
this.allowUniversalAccessFromFileURLs = false,
this.appCachePath,
this.blockNetworkImage = false,
this.blockNetworkLoads = false,
this.cacheMode = AndroidInAppWebViewCacheMode.LOAD_DEFAULT,
this.cursiveFontFamily = "cursive",
this.defaultFixedFontSize = 16,
this.defaultFontSize = 16,
this.defaultTextEncodingName = "UTF-8",
this.disabledActionModeMenuItems,
this.fantasyFontFamily = "fantasy",
this.fixedFontFamily = "monospace",
this.forceDark = AndroidInAppWebViewForceDark.FORCE_DARK_OFF,
this.geolocationEnabled = true,
this.layoutAlgorithm,
this.loadWithOverviewMode = true,
this.loadsImagesAutomatically = true,
this.minimumLogicalFontSize = 8,
this.needInitialFocus = true,
this.offscreenPreRaster = false,
this.sansSerifFontFamily = "sans-serif",
this.serifFontFamily = "sans-serif",
this.standardFontFamily = "sans-serif",
this.saveFormData = true,
this.thirdPartyCookiesEnabled = true,
this.hardwareAcceleration = true,
this.initialScale = 0});
@override @override
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
@ -369,7 +493,6 @@ class AndroidInAppWebViewOptions implements WebViewOptions, BrowserOptions, Andr
}; };
} }
@override
static AndroidInAppWebViewOptions fromMap(Map<String, dynamic> map) { static AndroidInAppWebViewOptions fromMap(Map<String, dynamic> map) {
AndroidInAppWebViewOptions options = new AndroidInAppWebViewOptions(); AndroidInAppWebViewOptions options = new AndroidInAppWebViewOptions();
options.textZoom = map["textZoom"]; options.textZoom = map["textZoom"];
@ -381,25 +504,32 @@ class AndroidInAppWebViewOptions implements WebViewOptions, BrowserOptions, Andr
options.domStorageEnabled = map["domStorageEnabled"]; options.domStorageEnabled = map["domStorageEnabled"];
options.useWideViewPort = map["useWideViewPort"]; options.useWideViewPort = map["useWideViewPort"];
options.safeBrowsingEnabled = map["safeBrowsingEnabled"]; options.safeBrowsingEnabled = map["safeBrowsingEnabled"];
options.mixedContentMode = AndroidInAppWebViewMixedContentMode.fromValue(map["mixedContentMode"]); options.mixedContentMode =
AndroidInAppWebViewMixedContentMode.fromValue(map["mixedContentMode"]);
options.allowContentAccess = map["allowContentAccess"]; options.allowContentAccess = map["allowContentAccess"];
options.allowFileAccess = map["allowFileAccess"]; options.allowFileAccess = map["allowFileAccess"];
options.allowFileAccessFromFileURLs = map["allowFileAccessFromFileURLs"]; options.allowFileAccessFromFileURLs = map["allowFileAccessFromFileURLs"];
options.allowUniversalAccessFromFileURLs = map["allowUniversalAccessFromFileURLs"]; options.allowUniversalAccessFromFileURLs =
map["allowUniversalAccessFromFileURLs"];
options.appCachePath = map["appCachePath"]; options.appCachePath = map["appCachePath"];
options.blockNetworkImage = map["blockNetworkImage"]; options.blockNetworkImage = map["blockNetworkImage"];
options.blockNetworkLoads = map["blockNetworkLoads"]; options.blockNetworkLoads = map["blockNetworkLoads"];
options.cacheMode = AndroidInAppWebViewCacheMode.fromValue(map["cacheMode"]); options.cacheMode =
AndroidInAppWebViewCacheMode.fromValue(map["cacheMode"]);
options.cursiveFontFamily = map["cursiveFontFamily"]; options.cursiveFontFamily = map["cursiveFontFamily"];
options.defaultFixedFontSize = map["defaultFixedFontSize"]; options.defaultFixedFontSize = map["defaultFixedFontSize"];
options.defaultFontSize = map["defaultFontSize"]; options.defaultFontSize = map["defaultFontSize"];
options.defaultTextEncodingName = map["defaultTextEncodingName"]; options.defaultTextEncodingName = map["defaultTextEncodingName"];
options.disabledActionModeMenuItems = AndroidInAppWebViewModeMenuItem.fromValue(map["disabledActionModeMenuItems"]); options.disabledActionModeMenuItems =
AndroidInAppWebViewModeMenuItem.fromValue(
map["disabledActionModeMenuItems"]);
options.fantasyFontFamily = map["fantasyFontFamily"]; options.fantasyFontFamily = map["fantasyFontFamily"];
options.fixedFontFamily = map["fixedFontFamily"]; options.fixedFontFamily = map["fixedFontFamily"];
options.forceDark = AndroidInAppWebViewForceDark.fromValue(map["forceDark"]); options.forceDark =
AndroidInAppWebViewForceDark.fromValue(map["forceDark"]);
options.geolocationEnabled = map["geolocationEnabled"]; options.geolocationEnabled = map["geolocationEnabled"];
options.layoutAlgorithm = AndroidInAppWebViewLayoutAlgorithm.fromValue(map["layoutAlgorithm"]); options.layoutAlgorithm =
AndroidInAppWebViewLayoutAlgorithm.fromValue(map["layoutAlgorithm"]);
options.loadWithOverviewMode = map["loadWithOverviewMode"]; options.loadWithOverviewMode = map["loadWithOverviewMode"];
options.loadsImagesAutomatically = map["loadsImagesAutomatically"]; options.loadsImagesAutomatically = map["loadsImagesAutomatically"];
options.minimumLogicalFontSize = map["minimumLogicalFontSize"]; options.minimumLogicalFontSize = map["minimumLogicalFontSize"];
@ -417,57 +547,79 @@ class AndroidInAppWebViewOptions implements WebViewOptions, BrowserOptions, Andr
} }
///This class represents all the iOS-only WebView options available. ///This class represents all the iOS-only WebView options available.
class IosInAppWebViewOptions implements WebViewOptions, BrowserOptions, IosOptions { class IosInAppWebViewOptions
implements WebViewOptions, BrowserOptions, IosOptions {
///Set to `true` to disable the bouncing of the WebView when the scrolling has reached an edge of the content. The default value is `false`. ///Set to `true` to disable the bouncing of the WebView when the scrolling has reached an edge of the content. The default value is `false`.
bool disallowOverScroll; bool disallowOverScroll;
///Set to `true` to allow a viewport meta tag to either disable or restrict the range of user scaling. The default value is `false`. ///Set to `true` to allow a viewport meta tag to either disable or restrict the range of user scaling. The default value is `false`.
bool enableViewportScale; bool enableViewportScale;
///Set to `true` if you want the WebView suppresses content rendering until it is fully loaded into memory. The default value is `false`. ///Set to `true` if you want the WebView suppresses content rendering until it is fully loaded into memory. The default value is `false`.
bool suppressesIncrementalRendering; bool suppressesIncrementalRendering;
///Set to `true` to allow AirPlay. The default value is `true`. ///Set to `true` to allow AirPlay. The default value is `true`.
bool allowsAirPlayForMediaPlayback; bool allowsAirPlayForMediaPlayback;
///Set to `true` to allow the horizontal swipe gestures trigger back-forward list navigations. The default value is `true`. ///Set to `true` to allow the horizontal swipe gestures trigger back-forward list navigations. The default value is `true`.
bool allowsBackForwardNavigationGestures; bool allowsBackForwardNavigationGestures;
///Set to `true` to allow that pressing on a link displays a preview of the destination for the link. The default value is `true`. ///Set to `true` to allow that pressing on a link displays a preview of the destination for the link. The default value is `true`.
/// ///
///**NOTE**: available on iOS 9.0+. ///**NOTE**: available on iOS 9.0+.
bool allowsLinkPreview; bool allowsLinkPreview;
///Set to `true` if you want that the WebView should always allow scaling of the webpage, regardless of the author's intent. ///Set to `true` if you want that the WebView should always allow scaling of the webpage, regardless of the author's intent.
///The ignoresViewportScaleLimits property overrides the `user-scalable` HTML property in a webpage. The default value is `false`. ///The ignoresViewportScaleLimits property overrides the `user-scalable` HTML property in a webpage. The default value is `false`.
bool ignoresViewportScaleLimits; bool ignoresViewportScaleLimits;
///Set to `true` to allow HTML5 media playback to appear inline within the screen layout, using browser-supplied controls rather than native controls. ///Set to `true` to allow HTML5 media playback to appear inline within the screen layout, using browser-supplied controls rather than native controls.
///For this to work, add the `webkit-playsinline` attribute to any `<video>` elements. The default value is `false`. ///For this to work, add the `webkit-playsinline` attribute to any `<video>` elements. The default value is `false`.
bool allowsInlineMediaPlayback; bool allowsInlineMediaPlayback;
///Set to `true` to allow HTML5 videos play picture-in-picture. The default value is `true`. ///Set to `true` to allow HTML5 videos play picture-in-picture. The default value is `true`.
/// ///
///**NOTE**: available on iOS 9.0+. ///**NOTE**: available on iOS 9.0+.
bool allowsPictureInPictureMediaPlayback; bool allowsPictureInPictureMediaPlayback;
///A Boolean value indicating whether warnings should be shown for suspected fraudulent content such as phishing or malware. ///A Boolean value indicating whether warnings should be shown for suspected fraudulent content such as phishing or malware.
///According to the official documentation, this feature is currently available in the following region: China. ///According to the official documentation, this feature is currently available in the following region: China.
///The default value is `true`. ///The default value is `true`.
/// ///
///**NOTE**: available on iOS 13.0+. ///**NOTE**: available on iOS 13.0+.
bool isFraudulentWebsiteWarningEnabled; bool isFraudulentWebsiteWarningEnabled;
///The level of granularity with which the user can interactively select content in the web view. ///The level of granularity with which the user can interactively select content in the web view.
///The default value is [IosInAppWebViewSelectionGranularity.DYNAMIC] ///The default value is [IosInAppWebViewSelectionGranularity.DYNAMIC]
IosInAppWebViewSelectionGranularity selectionGranularity; IosInAppWebViewSelectionGranularity selectionGranularity;
///Specifying a dataDetectoryTypes value adds interactivity to web content that matches the value. ///Specifying a dataDetectoryTypes value adds interactivity to web content that matches the value.
///For example, Safari adds a link to apple.com in the text Visit apple.com if the dataDetectorTypes property is set to [IosInAppWebViewDataDetectorTypes.LINK]. ///For example, Safari adds a link to apple.com in the text Visit apple.com if the dataDetectorTypes property is set to [IosInAppWebViewDataDetectorTypes.LINK].
///The default value is [IosInAppWebViewDataDetectorTypes.NONE]. ///The default value is [IosInAppWebViewDataDetectorTypes.NONE].
/// ///
///**NOTE**: available on iOS 10.0+. ///**NOTE**: available on iOS 10.0+.
List<IosInAppWebViewDataDetectorTypes> dataDetectorTypes; List<IosInAppWebViewDataDetectorTypes> dataDetectorTypes;
///Set `true` if shared cookies from `HTTPCookieStorage.shared` should used for every load request in the WebView. ///Set `true` if shared cookies from `HTTPCookieStorage.shared` should used for every load request in the WebView.
///The default value is `false`. ///The default value is `false`.
/// ///
///**NOTE**: available on iOS 11.0+. ///**NOTE**: available on iOS 11.0+.
bool sharedCookiesEnabled; bool sharedCookiesEnabled;
IosInAppWebViewOptions({this.disallowOverScroll = false, this.enableViewportScale = false, this.suppressesIncrementalRendering = false, this.allowsAirPlayForMediaPlayback = true, IosInAppWebViewOptions(
this.allowsBackForwardNavigationGestures = true, this.allowsLinkPreview = true, this.ignoresViewportScaleLimits = false, this.allowsInlineMediaPlayback = false, {this.disallowOverScroll = false,
this.allowsPictureInPictureMediaPlayback = true, this.isFraudulentWebsiteWarningEnabled = true, this.enableViewportScale = false,
this.selectionGranularity = IosInAppWebViewSelectionGranularity.DYNAMIC, this.dataDetectorTypes = const [IosInAppWebViewDataDetectorTypes.NONE], this.sharedCookiesEnabled = false this.suppressesIncrementalRendering = false,
}); this.allowsAirPlayForMediaPlayback = true,
this.allowsBackForwardNavigationGestures = true,
this.allowsLinkPreview = true,
this.ignoresViewportScaleLimits = false,
this.allowsInlineMediaPlayback = false,
this.allowsPictureInPictureMediaPlayback = true,
this.isFraudulentWebsiteWarningEnabled = true,
this.selectionGranularity = IosInAppWebViewSelectionGranularity.DYNAMIC,
this.dataDetectorTypes = const [IosInAppWebViewDataDetectorTypes.NONE],
this.sharedCookiesEnabled = false});
@override @override
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
@ -481,11 +633,13 @@ class IosInAppWebViewOptions implements WebViewOptions, BrowserOptions, IosOptio
"enableViewportScale": enableViewportScale, "enableViewportScale": enableViewportScale,
"suppressesIncrementalRendering": suppressesIncrementalRendering, "suppressesIncrementalRendering": suppressesIncrementalRendering,
"allowsAirPlayForMediaPlayback": allowsAirPlayForMediaPlayback, "allowsAirPlayForMediaPlayback": allowsAirPlayForMediaPlayback,
"allowsBackForwardNavigationGestures": allowsBackForwardNavigationGestures, "allowsBackForwardNavigationGestures":
allowsBackForwardNavigationGestures,
"allowsLinkPreview": allowsLinkPreview, "allowsLinkPreview": allowsLinkPreview,
"ignoresViewportScaleLimits": ignoresViewportScaleLimits, "ignoresViewportScaleLimits": ignoresViewportScaleLimits,
"allowsInlineMediaPlayback": allowsInlineMediaPlayback, "allowsInlineMediaPlayback": allowsInlineMediaPlayback,
"allowsPictureInPictureMediaPlayback": allowsPictureInPictureMediaPlayback, "allowsPictureInPictureMediaPlayback":
allowsPictureInPictureMediaPlayback,
"isFraudulentWebsiteWarningEnabled": isFraudulentWebsiteWarningEnabled, "isFraudulentWebsiteWarningEnabled": isFraudulentWebsiteWarningEnabled,
"selectionGranularity": selectionGranularity.toValue(), "selectionGranularity": selectionGranularity.toValue(),
"dataDetectorTypes": dataDetectorTypesList, "dataDetectorTypes": dataDetectorTypesList,
@ -493,26 +647,34 @@ class IosInAppWebViewOptions implements WebViewOptions, BrowserOptions, IosOptio
}; };
} }
@override
static IosInAppWebViewOptions fromMap(Map<String, dynamic> map) { static IosInAppWebViewOptions fromMap(Map<String, dynamic> map) {
List<IosInAppWebViewDataDetectorTypes> dataDetectorTypes = []; List<IosInAppWebViewDataDetectorTypes> dataDetectorTypes = [];
List<String> dataDetectorTypesList = List<String>.from(map["dataDetectorTypes"] ?? []); List<String> dataDetectorTypesList =
List<String>.from(map["dataDetectorTypes"] ?? []);
dataDetectorTypesList.forEach((dataDetectorType) { dataDetectorTypesList.forEach((dataDetectorType) {
dataDetectorTypes.add(IosInAppWebViewDataDetectorTypes.fromValue(dataDetectorType)); dataDetectorTypes
.add(IosInAppWebViewDataDetectorTypes.fromValue(dataDetectorType));
}); });
IosInAppWebViewOptions options = new IosInAppWebViewOptions(); IosInAppWebViewOptions options = new IosInAppWebViewOptions();
options.disallowOverScroll = map["disallowOverScroll"]; options.disallowOverScroll = map["disallowOverScroll"];
options.enableViewportScale = map["enableViewportScale"]; options.enableViewportScale = map["enableViewportScale"];
options.suppressesIncrementalRendering = map["suppressesIncrementalRendering"]; options.suppressesIncrementalRendering =
options.allowsAirPlayForMediaPlayback = map["allowsAirPlayForMediaPlayback"]; map["suppressesIncrementalRendering"];
options.allowsBackForwardNavigationGestures = map["allowsBackForwardNavigationGestures"]; options.allowsAirPlayForMediaPlayback =
map["allowsAirPlayForMediaPlayback"];
options.allowsBackForwardNavigationGestures =
map["allowsBackForwardNavigationGestures"];
options.allowsLinkPreview = map["allowsLinkPreview"]; options.allowsLinkPreview = map["allowsLinkPreview"];
options.ignoresViewportScaleLimits = map["ignoresViewportScaleLimits"]; options.ignoresViewportScaleLimits = map["ignoresViewportScaleLimits"];
options.allowsInlineMediaPlayback = map["allowsInlineMediaPlayback"]; options.allowsInlineMediaPlayback = map["allowsInlineMediaPlayback"];
options.allowsPictureInPictureMediaPlayback = map["allowsPictureInPictureMediaPlayback"]; options.allowsPictureInPictureMediaPlayback =
options.isFraudulentWebsiteWarningEnabled = map["isFraudulentWebsiteWarningEnabled"]; map["allowsPictureInPictureMediaPlayback"];
options.selectionGranularity = IosInAppWebViewSelectionGranularity.fromValue(map["selectionGranularity"]); options.isFraudulentWebsiteWarningEnabled =
map["isFraudulentWebsiteWarningEnabled"];
options.selectionGranularity =
IosInAppWebViewSelectionGranularity.fromValue(
map["selectionGranularity"]);
options.dataDetectorTypes = dataDetectorTypes; options.dataDetectorTypes = dataDetectorTypes;
options.sharedCookiesEnabled = map["sharedCookiesEnabled"]; options.sharedCookiesEnabled = map["sharedCookiesEnabled"];
return options; return options;
@ -520,18 +682,26 @@ class IosInAppWebViewOptions implements WebViewOptions, BrowserOptions, IosOptio
} }
///This class represents all the cross-platform [InAppBrowser] options available. ///This class represents all the cross-platform [InAppBrowser] options available.
class InAppBrowserOptions implements BrowserOptions, AndroidOptions, IosOptions { class InAppBrowserOptions
implements BrowserOptions, AndroidOptions, IosOptions {
///Set to `true` to create the browser and load the page, but not show it. Omit or set to `false` to have the browser open and load normally. ///Set to `true` to create the browser and load the page, but not show it. Omit or set to `false` to have the browser open and load normally.
///The default value is `false`. ///The default value is `false`.
bool hidden; bool hidden;
///Set to `false` to hide the toolbar at the top of the WebView. The default value is `true`. ///Set to `false` to hide the toolbar at the top of the WebView. The default value is `true`.
bool toolbarTop; bool toolbarTop;
///Set the custom background color of the toolbar at the top. ///Set the custom background color of the toolbar at the top.
String toolbarTopBackgroundColor; String toolbarTopBackgroundColor;
///Set to `true` to hide the url bar on the toolbar at the top. The default value is `false`. ///Set to `true` to hide the url bar on the toolbar at the top. The default value is `false`.
bool hideUrlBar; bool hideUrlBar;
InAppBrowserOptions({this.hidden = false, this.toolbarTop = true, this.toolbarTopBackgroundColor = "", this.hideUrlBar = false}); InAppBrowserOptions(
{this.hidden = false,
this.toolbarTop = true,
this.toolbarTopBackgroundColor = "",
this.hideUrlBar = false});
@override @override
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
@ -543,7 +713,6 @@ class InAppBrowserOptions implements BrowserOptions, AndroidOptions, IosOptions
}; };
} }
@override
static InAppBrowserOptions fromMap(Map<String, dynamic> map) { static InAppBrowserOptions fromMap(Map<String, dynamic> map) {
InAppBrowserOptions options = new InAppBrowserOptions(); InAppBrowserOptions options = new InAppBrowserOptions();
options.hidden = map["hidden"]; options.hidden = map["hidden"];
@ -558,14 +727,21 @@ class InAppBrowserOptions implements BrowserOptions, AndroidOptions, IosOptions
class AndroidInAppBrowserOptions implements BrowserOptions, AndroidOptions { class AndroidInAppBrowserOptions implements BrowserOptions, AndroidOptions {
///Set to `true` if you want the title should be displayed. The default value is `false`. ///Set to `true` if you want the title should be displayed. The default value is `false`.
bool hideTitleBar; bool hideTitleBar;
///Set the action bar's title. ///Set the action bar's title.
String toolbarTopFixedTitle; String toolbarTopFixedTitle;
///Set to `false` to not close the InAppBrowser when the user click on the back button and the WebView cannot go back to the history. The default value is `true`. ///Set to `false` to not close the InAppBrowser when the user click on the back button and the WebView cannot go back to the history. The default value is `true`.
bool closeOnCannotGoBack; bool closeOnCannotGoBack;
///Set to `false` to hide the progress bar at the bottom of the toolbar at the top. The default value is `true`. ///Set to `false` to hide the progress bar at the bottom of the toolbar at the top. The default value is `true`.
bool progressBar; bool progressBar;
AndroidInAppBrowserOptions({this.hideTitleBar = true, this.toolbarTopFixedTitle = "", this.closeOnCannotGoBack = true, this.progressBar = true}); AndroidInAppBrowserOptions(
{this.hideTitleBar = true,
this.toolbarTopFixedTitle = "",
this.closeOnCannotGoBack = true,
this.progressBar = true});
@override @override
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
@ -577,7 +753,6 @@ class AndroidInAppBrowserOptions implements BrowserOptions, AndroidOptions {
}; };
} }
@override
static AndroidInAppBrowserOptions fromMap(Map<String, dynamic> map) { static AndroidInAppBrowserOptions fromMap(Map<String, dynamic> map) {
AndroidInAppBrowserOptions options = new AndroidInAppBrowserOptions(); AndroidInAppBrowserOptions options = new AndroidInAppBrowserOptions();
options.hideTitleBar = map["hideTitleBar"]; options.hideTitleBar = map["hideTitleBar"];
@ -592,24 +767,37 @@ class AndroidInAppBrowserOptions implements BrowserOptions, AndroidOptions {
class IosInAppBrowserOptions implements BrowserOptions, IosOptions { class IosInAppBrowserOptions implements BrowserOptions, IosOptions {
///Set to `false` to hide the toolbar at the bottom of the WebView. The default value is `true`. ///Set to `false` to hide the toolbar at the bottom of the WebView. The default value is `true`.
bool toolbarBottom; bool toolbarBottom;
///Set the custom background color of the toolbar at the bottom. ///Set the custom background color of the toolbar at the bottom.
String toolbarBottomBackgroundColor; String toolbarBottomBackgroundColor;
///Set to `true` to set the toolbar at the bottom translucent. The default value is `true`. ///Set to `true` to set the toolbar at the bottom translucent. The default value is `true`.
bool toolbarBottomTranslucent; bool toolbarBottomTranslucent;
///Set the custom text for the close button. ///Set the custom text for the close button.
String closeButtonCaption; String closeButtonCaption;
///Set the custom color for the close button. ///Set the custom color for the close button.
String closeButtonColor; String closeButtonColor;
///Set the custom modal presentation style when presenting the WebView. The default value is [IosWebViewOptionsPresentationStyle.FULL_SCREEN]. ///Set the custom modal presentation style when presenting the WebView. The default value is [IosWebViewOptionsPresentationStyle.FULL_SCREEN].
IosWebViewOptionsPresentationStyle presentationStyle; IosWebViewOptionsPresentationStyle presentationStyle;
///Set to the custom transition style when presenting the WebView. The default value is [IosWebViewOptionsTransitionStyle.COVER_VERTICAL]. ///Set to the custom transition style when presenting the WebView. The default value is [IosWebViewOptionsTransitionStyle.COVER_VERTICAL].
IosWebViewOptionsTransitionStyle transitionStyle; IosWebViewOptionsTransitionStyle transitionStyle;
///Set to `false` to hide the spinner when the WebView is loading a page. The default value is `true`. ///Set to `false` to hide the spinner when the WebView is loading a page. The default value is `true`.
bool spinner; bool spinner;
IosInAppBrowserOptions({this.toolbarBottom = true, this.toolbarBottomBackgroundColor = "", this.toolbarBottomTranslucent = true, this.closeButtonCaption = "", IosInAppBrowserOptions(
this.closeButtonColor = "", this.presentationStyle = IosWebViewOptionsPresentationStyle.FULL_SCREEN, {this.toolbarBottom = true,
this.transitionStyle = IosWebViewOptionsTransitionStyle.COVER_VERTICAL, this.spinner = true}); this.toolbarBottomBackgroundColor = "",
this.toolbarBottomTranslucent = true,
this.closeButtonCaption = "",
this.closeButtonColor = "",
this.presentationStyle = IosWebViewOptionsPresentationStyle.FULL_SCREEN,
this.transitionStyle = IosWebViewOptionsTransitionStyle.COVER_VERTICAL,
this.spinner = true});
@override @override
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
@ -625,7 +813,6 @@ class IosInAppBrowserOptions implements BrowserOptions, IosOptions {
}; };
} }
@override
static IosInAppBrowserOptions fromMap(Map<String, dynamic> map) { static IosInAppBrowserOptions fromMap(Map<String, dynamic> map) {
IosInAppBrowserOptions options = new IosInAppBrowserOptions(); IosInAppBrowserOptions options = new IosInAppBrowserOptions();
options.toolbarBottom = map["toolbarBottom"]; options.toolbarBottom = map["toolbarBottom"];
@ -633,27 +820,39 @@ class IosInAppBrowserOptions implements BrowserOptions, IosOptions {
options.toolbarBottomTranslucent = map["toolbarBottomTranslucent"]; options.toolbarBottomTranslucent = map["toolbarBottomTranslucent"];
options.closeButtonCaption = map["closeButtonCaption"]; options.closeButtonCaption = map["closeButtonCaption"];
options.closeButtonColor = map["closeButtonColor"]; options.closeButtonColor = map["closeButtonColor"];
options.presentationStyle = IosWebViewOptionsPresentationStyle.fromValue(map["presentationStyle"]); options.presentationStyle =
options.transitionStyle = IosWebViewOptionsTransitionStyle.fromValue(map["transitionStyle"]); IosWebViewOptionsPresentationStyle.fromValue(map["presentationStyle"]);
options.transitionStyle =
IosWebViewOptionsTransitionStyle.fromValue(map["transitionStyle"]);
options.spinner = map["spinner"]; options.spinner = map["spinner"];
return options; return options;
} }
} }
///This class represents all the Android-only [ChromeSafariBrowser] options available. ///This class represents all the Android-only [ChromeSafariBrowser] options available.
class AndroidChromeCustomTabsOptions implements ChromeSafariBrowserOptions, AndroidOptions { class AndroidChromeCustomTabsOptions
implements ChromeSafariBrowserOptions, AndroidOptions {
///Set to `false` if you don't want the default share button. The default value is `true`. ///Set to `false` if you don't want the default share button. The default value is `true`.
bool addShareButton; bool addShareButton;
///Set to `false` if the title shouldn't be shown in the custom tab. The default value is `true`. ///Set to `false` if the title shouldn't be shown in the custom tab. The default value is `true`.
bool showTitle; bool showTitle;
///Set the custom background color of the toolbar. ///Set the custom background color of the toolbar.
String toolbarBackgroundColor; String toolbarBackgroundColor;
///Set to `true` to enable the url bar to hide as the user scrolls down on the page. The default value is `false`. ///Set to `true` to enable the url bar to hide as the user scrolls down on the page. The default value is `false`.
bool enableUrlBarHiding; bool enableUrlBarHiding;
///Set to `true` to enable Instant Apps. The default value is `false`. ///Set to `true` to enable Instant Apps. The default value is `false`.
bool instantAppsEnabled; bool instantAppsEnabled;
AndroidChromeCustomTabsOptions({this.addShareButton = true, this.showTitle = true, this.toolbarBackgroundColor = "", this.enableUrlBarHiding = false, this.instantAppsEnabled = false}); AndroidChromeCustomTabsOptions(
{this.addShareButton = true,
this.showTitle = true,
this.toolbarBackgroundColor = "",
this.enableUrlBarHiding = false,
this.instantAppsEnabled = false});
@override @override
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
@ -666,9 +865,9 @@ class AndroidChromeCustomTabsOptions implements ChromeSafariBrowserOptions, Andr
}; };
} }
@override
static AndroidChromeCustomTabsOptions fromMap(Map<String, dynamic> map) { static AndroidChromeCustomTabsOptions fromMap(Map<String, dynamic> map) {
AndroidChromeCustomTabsOptions options = new AndroidChromeCustomTabsOptions(); AndroidChromeCustomTabsOptions options =
new AndroidChromeCustomTabsOptions();
options.addShareButton = map["addShareButton"]; options.addShareButton = map["addShareButton"];
options.showTitle = map["showTitle"]; options.showTitle = map["showTitle"];
options.toolbarBackgroundColor = map["toolbarBackgroundColor"]; options.toolbarBackgroundColor = map["toolbarBackgroundColor"];
@ -682,28 +881,39 @@ class AndroidChromeCustomTabsOptions implements ChromeSafariBrowserOptions, Andr
class IosSafariOptions implements ChromeSafariBrowserOptions, IosOptions { class IosSafariOptions implements ChromeSafariBrowserOptions, IosOptions {
///Set to `true` if Reader mode should be entered automatically when it is available for the webpage. The default value is `false`. ///Set to `true` if Reader mode should be entered automatically when it is available for the webpage. The default value is `false`.
bool entersReaderIfAvailable; bool entersReaderIfAvailable;
///Set to `true` to enable bar collapsing. The default value is `false`. ///Set to `true` to enable bar collapsing. The default value is `false`.
bool barCollapsingEnabled; bool barCollapsingEnabled;
///Set the custom style for the dismiss button. The default value is [IosSafariOptionsDismissButtonStyle.DONE]. ///Set the custom style for the dismiss button. The default value is [IosSafariOptionsDismissButtonStyle.DONE].
/// ///
///**NOTE**: available on iOS 11.0+. ///**NOTE**: available on iOS 11.0+.
IosSafariOptionsDismissButtonStyle dismissButtonStyle; IosSafariOptionsDismissButtonStyle dismissButtonStyle;
///Set the custom background color of the navigation bar and the toolbar. ///Set the custom background color of the navigation bar and the toolbar.
/// ///
///**NOTE**: available on iOS 10.0+. ///**NOTE**: available on iOS 10.0+.
String preferredBarTintColor; String preferredBarTintColor;
///Set the custom color of the control buttons on the navigation bar and the toolbar. ///Set the custom color of the control buttons on the navigation bar and the toolbar.
/// ///
///**NOTE**: available on iOS 10.0+. ///**NOTE**: available on iOS 10.0+.
String preferredControlTintColor; String preferredControlTintColor;
///Set the custom modal presentation style when presenting the WebView. The default value is [IosWebViewOptionsPresentationStyle.FULL_SCREEN]. ///Set the custom modal presentation style when presenting the WebView. The default value is [IosWebViewOptionsPresentationStyle.FULL_SCREEN].
IosWebViewOptionsPresentationStyle presentationStyle; IosWebViewOptionsPresentationStyle presentationStyle;
///Set to the custom transition style when presenting the WebView. The default value is [IosWebViewOptionsTransitionStyle.COVER_VERTICAL]. ///Set to the custom transition style when presenting the WebView. The default value is [IosWebViewOptionsTransitionStyle.COVER_VERTICAL].
IosWebViewOptionsTransitionStyle transitionStyle; IosWebViewOptionsTransitionStyle transitionStyle;
IosSafariOptions({this.entersReaderIfAvailable = false, this.barCollapsingEnabled = false, this.dismissButtonStyle = IosSafariOptionsDismissButtonStyle.DONE, IosSafariOptions(
this.preferredBarTintColor = "", this.preferredControlTintColor = "", this.presentationStyle = IosWebViewOptionsPresentationStyle.FULL_SCREEN, {this.entersReaderIfAvailable = false,
this.transitionStyle = IosWebViewOptionsTransitionStyle.COVER_VERTICAL}); this.barCollapsingEnabled = false,
this.dismissButtonStyle = IosSafariOptionsDismissButtonStyle.DONE,
this.preferredBarTintColor = "",
this.preferredControlTintColor = "",
this.presentationStyle = IosWebViewOptionsPresentationStyle.FULL_SCREEN,
this.transitionStyle = IosWebViewOptionsTransitionStyle.COVER_VERTICAL});
@override @override
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
@ -718,16 +928,18 @@ class IosSafariOptions implements ChromeSafariBrowserOptions, IosOptions {
}; };
} }
@override
static IosSafariOptions fromMap(Map<String, dynamic> map) { static IosSafariOptions fromMap(Map<String, dynamic> map) {
IosSafariOptions options = new IosSafariOptions(); IosSafariOptions options = new IosSafariOptions();
options.entersReaderIfAvailable = map["entersReaderIfAvailable"]; options.entersReaderIfAvailable = map["entersReaderIfAvailable"];
options.barCollapsingEnabled = map["barCollapsingEnabled"]; options.barCollapsingEnabled = map["barCollapsingEnabled"];
options.dismissButtonStyle = IosSafariOptionsDismissButtonStyle.fromValue(map["dismissButtonStyle"]); options.dismissButtonStyle =
IosSafariOptionsDismissButtonStyle.fromValue(map["dismissButtonStyle"]);
options.preferredBarTintColor = map["preferredBarTintColor"]; options.preferredBarTintColor = map["preferredBarTintColor"];
options.preferredControlTintColor = map["preferredControlTintColor"]; options.preferredControlTintColor = map["preferredControlTintColor"];
options.presentationStyle = IosWebViewOptionsPresentationStyle.fromValue(map["presentationStyle"]); options.presentationStyle =
options.transitionStyle = IosWebViewOptionsTransitionStyle.fromValue(map["transitionStyle"]); IosWebViewOptionsPresentationStyle.fromValue(map["presentationStyle"]);
options.transitionStyle =
IosWebViewOptionsTransitionStyle.fromValue(map["transitionStyle"]);
return options; return options;
} }
} }

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: 2.0.1 version: 2.0.1+1
author: Lorenzo Pichilli <pichillilorenzo@gmail.com> author: Lorenzo Pichilli <pichillilorenzo@gmail.com>
homepage: https://github.com/pichillilorenzo/flutter_inappwebview homepage: https://github.com/pichillilorenzo/flutter_inappwebview